| 20 |
20 |
** 009 IMS 20250704 The mouseOver modifier has been changed from private to protected. Rfs:#9973.
|
| 21 |
21 |
** 010 EVL 20260105 Improved bounds check for mouseExited event to avoid regressions. We need to check if
|
| 22 |
22 |
** the mouse point is inside possible tooltip overlay window that can overlap the button.
|
|
23 |
** PMP 20260127 TODO
|
| 23 |
24 |
*/
|
| 24 |
25 |
|
| 25 |
26 |
/*
|
| ... | ... | |
| 85 |
86 |
import com.goldencode.p2j.ui.client.widget.Container;
|
| 86 |
87 |
|
| 87 |
88 |
import java.awt.event.*;
|
| 88 |
|
import java.util.concurrent.*;
|
| 89 |
89 |
import java.util.logging.*;
|
| 90 |
90 |
|
| 91 |
91 |
/**
|
| ... | ... | |
| 109 |
109 |
/** The mouse is dragging */
|
| 110 |
110 |
private boolean mouseDragging;
|
| 111 |
111 |
|
|
112 |
/** TODO */
|
|
113 |
private Runnable repaintListener = null;
|
|
114 |
|
|
115 |
/** TODO */
|
|
116 |
private boolean suppressNextEnterRepaint = false;
|
|
117 |
|
| 112 |
118 |
/**
|
| 113 |
119 |
* This constructor creates a button with a given text label.
|
| 114 |
120 |
*
|
| ... | ... | |
| 151 |
157 |
}
|
| 152 |
158 |
|
| 153 |
159 |
/**
|
|
160 |
* TODO
|
|
161 |
*
|
|
162 |
* @param listener
|
|
163 |
* TODO
|
|
164 |
*/
|
|
165 |
public void setRepaintListener(Runnable listener)
|
|
166 |
{
|
|
167 |
repaintListener = listener;
|
|
168 |
}
|
|
169 |
|
|
170 |
/**
|
|
171 |
* TODO
|
|
172 |
*/
|
|
173 |
public void runRepaintListener()
|
|
174 |
{
|
|
175 |
if (repaintListener != null)
|
|
176 |
{
|
|
177 |
repaintListener.run();
|
|
178 |
}
|
|
179 |
|
|
180 |
clearRepaintListener();
|
|
181 |
clearHoverSuppression();
|
|
182 |
}
|
|
183 |
|
|
184 |
/**
|
|
185 |
* TODO
|
|
186 |
*/
|
|
187 |
public void clearHoverSuppression()
|
|
188 |
{
|
|
189 |
suppressNextEnterRepaint = false;
|
|
190 |
}
|
|
191 |
|
|
192 |
public void clearRepaintListener()
|
|
193 |
{
|
|
194 |
repaintListener = null;
|
|
195 |
}
|
|
196 |
|
|
197 |
/**
|
| 154 |
198 |
* Process the notification of a mouse enter event occurred for this widget. Store the new
|
| 155 |
199 |
* state and call repaint to update the button's visual.
|
| 156 |
200 |
*
|
| ... | ... | |
| 163 |
207 |
if (isEnabled())
|
| 164 |
208 |
{
|
| 165 |
209 |
this.mouseOver = true;
|
| 166 |
|
if (ThemeManager.getCurrentTheme().isMouseHoverSensitive())
|
|
210 |
// TODO:
|
|
211 |
if (ThemeManager.getCurrentTheme().isMouseHoverSensitive() && !suppressNextEnterRepaint)
|
| 167 |
212 |
{
|
| 168 |
213 |
repaint();
|
| 169 |
214 |
}
|
|
215 |
clearHoverSuppression();
|
| 170 |
216 |
}
|
| 171 |
217 |
}
|
| 172 |
218 |
|
| ... | ... | |
| 187 |
233 |
|
| 188 |
234 |
final int xMouse = e.getXOnScreen();
|
| 189 |
235 |
final int yMouse = e.getYOnScreen();
|
| 190 |
|
|
| 191 |
|
processMouseExited(xMouse, yMouse);
|
|
236 |
|
|
237 |
// for certain cases we need to check if we are still inside the button
|
|
238 |
NativePoint p = this.displayPhysicalLocation();
|
|
239 |
CoordinatesConversion cc = screen().coordinates();
|
|
240 |
NativeRectangle rect = cc.rectangleToNative(this.bounds());
|
|
241 |
|
|
242 |
final ToolTip tt = ToolTip.getInstance();
|
|
243 |
// only for buttons with tooltips
|
|
244 |
boolean skipBtnRepaint = xMouse >= p.x && xMouse <= p.x + rect.right() - rect.left() &&
|
|
245 |
yMouse >= p.y && yMouse <= p.y + rect.bottom() - rect.top() &&
|
|
246 |
tt != null && tt.isClockActive() && tt.isInsideTooltipArea(xMouse, yMouse);
|
|
247 |
|
|
248 |
if (ThemeManager.getCurrentTheme().isMouseHoverSensitive() && skipBtnRepaint)
|
|
249 |
{
|
|
250 |
clearRepaintListener();
|
|
251 |
setRepaintListener(() -> repaint());
|
|
252 |
suppressNextEnterRepaint = true;
|
|
253 |
return;
|
|
254 |
}
|
|
255 |
|
|
256 |
// normal exit
|
|
257 |
if (ThemeManager.getCurrentTheme().isMouseHoverSensitive())
|
|
258 |
{
|
|
259 |
repaint();
|
|
260 |
}
|
|
261 |
|
|
262 |
clearRepaintListener();
|
|
263 |
clearHoverSuppression();
|
| 192 |
264 |
}
|
| 193 |
265 |
|
| 194 |
266 |
/**
|
| ... | ... | |
| 248 |
320 |
repaint();
|
| 249 |
321 |
}
|
| 250 |
322 |
}
|
|
323 |
|
| 251 |
324 |
}
|
| 252 |
325 |
}
|
| 253 |
326 |
|
| ... | ... | |
| 332 |
405 |
MouseEvent.MOUSE_DRAGGED,
|
| 333 |
406 |
};
|
| 334 |
407 |
}
|
| 335 |
|
|
| 336 |
|
/**
|
| 337 |
|
* The actual mouse exit event handler. Can recall itself with forwarding the code to event loop.
|
| 338 |
|
*
|
| 339 |
|
* @param xMouse
|
| 340 |
|
* The mouse X absolute position.
|
| 341 |
|
* @param yMouse
|
| 342 |
|
* The mouse Y absolute position.
|
| 343 |
|
*/
|
| 344 |
|
private void processMouseExited(int xMouse, int yMouse)
|
| 345 |
|
{
|
| 346 |
|
// in this case we re-enter mouse over button
|
| 347 |
|
if (this.mouseOver)
|
| 348 |
|
{
|
| 349 |
|
return;
|
| 350 |
|
}
|
| 351 |
|
|
| 352 |
|
// for certain cases we need to check if we are still inside the button
|
| 353 |
|
NativePoint p = this.displayPhysicalLocation();
|
| 354 |
|
CoordinatesConversion cc = screen().coordinates();
|
| 355 |
|
NativeRectangle rect = cc.rectangleToNative(this.bounds());
|
| 356 |
|
|
| 357 |
|
final ToolTip tt = ToolTip.getInstance();
|
| 358 |
|
// only for buttons with tooltips
|
| 359 |
|
boolean skipBtnRepaint = xMouse >= p.x && xMouse <= p.x + rect.right() - rect.left() &&
|
| 360 |
|
yMouse >= p.y && yMouse <= p.y + rect.bottom() - rect.top() &&
|
| 361 |
|
tt != null && tt.isClockActive() && tt.isInsideTooltipArea(xMouse, yMouse);
|
| 362 |
|
|
| 363 |
|
// If the event occurs within the button bounds, the hover state should be maintained.
|
| 364 |
|
// this can happen for big enough button and currently active tooltip
|
| 365 |
|
if (ThemeManager.getCurrentTheme().isMouseHoverSensitive())
|
| 366 |
|
{
|
| 367 |
|
if (skipBtnRepaint)
|
| 368 |
|
{
|
| 369 |
|
// we have to repeat mouse exit handler until the mouse will become outside the button area
|
| 370 |
|
ThinClient tc = ThinClient.getInstance();
|
| 371 |
|
tc.invokeLaterOS(() ->
|
| 372 |
|
{
|
| 373 |
|
tc.eventDrawingBracket(this, () ->
|
| 374 |
|
{
|
| 375 |
|
NativePoint np = tc.getMousePosition();
|
| 376 |
|
processMouseExited(np.x, np.y);
|
| 377 |
|
});
|
| 378 |
|
}, 250L, TimeUnit.MILLISECONDS);
|
| 379 |
|
}
|
| 380 |
|
else
|
| 381 |
|
{
|
| 382 |
|
// immediate repaint
|
| 383 |
|
this.repaint();
|
| 384 |
|
}
|
| 385 |
|
}
|
| 386 |
|
}
|
| 387 |
408 |
}
|