1 /******************************************************************************* 2 * Copyright (c) 2018 Red Hat and others. All rights reserved. 3 * The contents of this file are made available under the terms 4 * of the GNU Lesser General Public License (LGPL) Version 2.1 that 5 * accompanies this distribution (lgpl-v21.txt). The LGPL is also 6 * available at http://www.gnu.org/licenses/lgpl.html. If the version 7 * of the LGPL at http://www.gnu.org is different to the version of 8 * the LGPL accompanying this distribution and there is any conflict 9 * between the two license versions, the terms of the LGPL accompanying 10 * this distribution shall govern. 11 * 12 * Contributors: 13 * Red Hat - initial API and implementation 14 *******************************************************************************/ 15 package org.eclipse.swt.tests.gtk.snippets; 16 17 import static org.junit.Assert.assertTrue; 18 19 import java.util.concurrent.atomic.AtomicInteger; 20 21 import org.eclipse.swt.SWT; 22 import org.eclipse.swt.events.ControlEvent; 23 import org.eclipse.swt.events.ControlListener; 24 import org.eclipse.swt.graphics.Rectangle; 25 import org.eclipse.swt.widgets.Button; 26 import org.eclipse.swt.widgets.Control; 27 import org.eclipse.swt.widgets.Display; 28 import org.eclipse.swt.widgets.Shell; 29 import org.junit.After; 30 import org.junit.Before; 31 import org.junit.Test; 32 33 34 /** 35 * Title: Bug 465280 – [GTK3] OS.gtk_widget_get_allocation returns (0,0) for invisible controls 36 * How to run: These are jUnits. Select the class and run as jUnits. 37 * Bug description: getBounds is not working properly in combination with setVisible. 38 * Expected results: All tests should pass, but on Gtk3.8+ the "fails_*" tests fail. 39 * GTK version(s): GTK3.8+ 40 * 41 * This is a snippet to validate upcomming bug submission. These will be used to make new jUnits later. 42 */ 43 public class Bug497705_setBoundsAfterSetVisible { 44 45 boolean debugShowWidget = false; // true = see shell & widget. False = tests run without interaction. 46 47 Display display; 48 Shell shell ; 49 private StringBuilder log; 50 private int x; 51 private int y; 52 private int height; 53 private int width; 54 private boolean passed; 55 private Rectangle bounds; 56 Control testControl; 57 58 @Before setUp()59 public void setUp() { 60 display = Display.getDefault(); 61 shell = new Shell(display); 62 shell.setSize(400, 400); 63 log = new StringBuilder(""); 64 x = 5; 65 y = 10; 66 height = 100; 67 width = 200; 68 passed = true; 69 testControl = new Button(shell, SWT.PUSH); 70 } 71 72 @Test fails_test2_setBoundsAfterVisibility()73 public void fails_test2_setBoundsAfterVisibility() { // Works on Gtk2, Fails on Gtk3. 74 testControl.setVisible(false); 75 testControl.setVisible(true); 76 77 testControl.setBounds(x, y, width, height); 78 79 bounds = testControl.getBounds(); 80 verifyBounds(); 81 } 82 @Test fails_test2b_setBoundsInvisibleWidgets()83 public void fails_test2b_setBoundsInvisibleWidgets() { // Works on Gtk2, Fails on Gtk3. 84 testControl.setVisible(false); 85 86 testControl.setBounds(x, y, width, height); 87 88 bounds = testControl.getBounds(); 89 verifyBounds(); 90 } 91 92 93 @Test fails_test3_setBoundsBetweenVisibility()94 public void fails_test3_setBoundsBetweenVisibility() { // Works on Gtk2, Fails on Gtk3. 95 testControl.setVisible(false); 96 testControl.setBounds(x, y, width, height); 97 testControl.setVisible(true); 98 99 bounds = testControl.getBounds(); 100 verifyBounds(); 101 } 102 103 @Test fails_moveInnvisibleControl()104 public void fails_moveInnvisibleControl() { 105 testControl.setBounds(4, 4, 6, 6); 106 107 shell.open(); for (int i = 0; i < 500; i++) display.readAndDispatch(); 108 testControl.setVisible(false); 109 shell.open(); for (int i = 0; i < 500; i++) display.readAndDispatch(); 110 testControl.setBounds(x, y, width, height); 111 shell.open(); for (int i = 0; i < 500; i++) display.readAndDispatch(); 112 testControl.setVisible(true); 113 shell.open(); for (int i = 0; i < 500; i++) display.readAndDispatch(); 114 115 bounds = testControl.getBounds(); // Visually looks ok. (width/height), but programatically incorrect getBounds(). 116 verifyBounds(); 117 } 118 119 @Test fails_unecessaryEvents()120 public void fails_unecessaryEvents() { // Breaks on Gtk3.8 & onwards 121 testControl.setVisible(false); 122 123 AtomicInteger resizeCount = new AtomicInteger(0); 124 AtomicInteger moveCount = new AtomicInteger(0); 125 126 testControl.addControlListener(new ControlListener() { 127 @Override 128 public void controlResized(ControlEvent e) { 129 resizeCount.incrementAndGet(); 130 } 131 @Override 132 public void controlMoved(ControlEvent e) { 133 moveCount.incrementAndGet(); 134 } 135 }); 136 137 for (int i = 0; i < 10; i++) { 138 testControl.setBounds(x, y, width, height); // Once bounds set, calling same bounds shouldn't trigger SWT.MOVE events. 139 } 140 if (resizeCount.get() != 1 || moveCount.get() != 1) { 141 passed = false; 142 log.append("\nERROR:\nExpected only one Resize and one Move event.\nActually received R/M:" + resizeCount.get() + "/" + moveCount.get()); 143 } 144 } 145 146 @Test works_test1_setBoundsBeforeVisibility()147 public void works_test1_setBoundsBeforeVisibility () { 148 // Note, here you can see that getBounds() does work for widgets that are set to be invisible, but 149 // only if setBounds was called before setVisible(false). 150 // The problem is inside setBounds, if it's called after setVisible(false), then getBounds() returns wrong output. 151 testControl.setBounds(x, y, width, height); 152 153 testControl.setVisible(false); 154 // testControl.setVisible(true); // commenting or uncommenting this doesn't change anything. 155 156 bounds = testControl.getBounds(); 157 verifyBounds(); 158 } 159 160 @Test works_zeroSize()161 public void works_zeroSize() { // There has to be a mechanism by which we set with and height to 0. 162 testControl.setBounds(x, y, width, height); 163 x = y = width = height = 0; 164 testControl.setBounds(x, y, width, height); 165 bounds = testControl.getBounds(); 166 verifyBounds(); 167 } 168 169 @Test works_drainingQueue()170 public void works_drainingQueue() { 171 testControl.setVisible(false); 172 testControl.setVisible(true); 173 174 // doing readAndDispatch up *before* 'setBounds()' doesn't make a difference. 175 testControl.setBounds(x, y, width, height); 176 177 // doing readAndDispatch *After* setBounds *many times* gives gtk time to update it's cache, and getBounds() returns correct coordinates. 178 shell.open(); 179 for (int i = 0; i < 1000; i++) 180 display.readAndDispatch(); 181 182 bounds = testControl.getBounds(); 183 verifyBounds(); 184 } 185 186 @After tearDown()187 public void tearDown() { 188 if (debugShowWidget) { 189 if (!passed) System.err.println(log.toString()); 190 191 shell.open(); 192 while (!shell.isDisposed()) { 193 if (!display.readAndDispatch()) 194 display.sleep(); 195 } 196 display.dispose(); 197 } 198 assertTrue(log.toString(), passed); 199 } 200 verifyBounds()201 private void verifyBounds() { 202 if (bounds.x != x | bounds.y != y) { 203 passed = false; 204 log.append("\nERROR: x,y do not match. Expected:" + x + "/" + y + " Actual:" + bounds.x + "/" + bounds.y); 205 } 206 if (bounds.height != height | bounds.width != width) { 207 passed = false; 208 log.append("\nERROR: width,height do not match. Expected:" + width + "/" + height + " Actual:" 209 + bounds.width + "/" + bounds.height); 210 } 211 } 212 213 } 214