113 */ 114 @IDProperty("id") 115 public class Tooltip extends PopupControl { 116 private static String TOOLTIP_PROP_KEY = "javafx.scene.control.Tooltip"; 117 118 // RT-31134 : the tooltip style includes a shadow around the tooltip with a 119 // width of 9 and height of 5. This causes mouse events to not reach the control 120 // underneath resulting in losing hover state on the control while the tooltip is showing. 121 // Displaying the tooltip at an offset indicated below resolves this issue. 122 // RT-37107: The y-offset was upped to 7 to ensure no overlaps when the tooltip 123 // is shown near the right edge of the screen. 124 private static int TOOLTIP_XOFFSET = 10; 125 private static int TOOLTIP_YOFFSET = 7; 126 127 private static TooltipBehavior BEHAVIOR = new TooltipBehavior(false); 128 129 /** 130 * Associates the given {@link Tooltip} with the given {@link Node}. The tooltip 131 * can then behave similar to when it is set on any {@link Control}. A single 132 * tooltip can be associated with multiple nodes. 133 * @see Tooltip 134 */ 135 public static void install(Node node, Tooltip t) { 136 BEHAVIOR.install(node, t); 137 } 138 139 /** 140 * Removes the association of the given {@link Tooltip} on the specified 141 * {@link Node}. Hence hovering on the node will no longer result in showing of the 142 * tooltip. 143 * @see Tooltip 144 */ 145 public static void uninstall(Node node, Tooltip t) { 146 BEHAVIOR.uninstall(node); 147 } 148 149 /*************************************************************************** 150 * * 151 * Constructors * 152 * * 153 **************************************************************************/ 154 155 /** 156 * Creates a tooltip with an empty string for its text. 157 */ 158 public Tooltip() { 159 this(null); 160 } 161 162 /** 163 * Creates a tooltip with the specified text. 164 * 165 * @param text A text string for the tooltip. 166 */ 167 public Tooltip(String text) { 168 super(); 169 if (text != null) setText(text); 170 bridge = new CSSBridge(); 171 PopupWindowHelper.getContent(this).setAll(bridge); 172 getStyleClass().setAll("tooltip"); 173 } 174 175 /*************************************************************************** 176 * * 177 * Properties * 178 * * 179 **************************************************************************/ 180 /** 181 * The text to display in the tooltip. If the text is set to null, an empty 182 * string will be displayed, despite the value being null. 183 */ 184 public final StringProperty textProperty() { return text; } 185 public final void setText(String value) { 186 textProperty().setValue(value); 187 } 188 public final String getText() { return text.getValue() == null ? "" : text.getValue(); } 189 private final StringProperty text = new SimpleStringProperty(this, "text", "") { 190 @Override protected void invalidated() { 191 super.invalidated(); 192 final String value = get(); 193 if (isShowing() && value != null && !value.equals(getText())) { 194 //Dynamic tooltip content is location-dependant. 195 //Chromium trick. 196 setAnchorX(BEHAVIOR.lastMouseX); 197 setAnchorY(BEHAVIOR.lastMouseY); 198 } 199 } 200 }; 201 202 /** 203 * Specifies the behavior for lines of text <em>when text is multiline</em>. 204 * Unlike {@link #contentDisplayProperty() contentDisplay} which affects the 205 * graphic and text, this setting only affects multiple lines of text 206 * relative to the text bounds. 207 */ 208 public final ObjectProperty<TextAlignment> textAlignmentProperty() { 209 return textAlignment; 210 } 211 public final void setTextAlignment(TextAlignment value) { 212 textAlignmentProperty().setValue(value); 213 } 214 public final TextAlignment getTextAlignment() { 215 return textAlignmentProperty().getValue(); 216 } 217 private final ObjectProperty<TextAlignment> textAlignment = 218 new SimpleStyleableObjectProperty<>(TEXT_ALIGNMENT, this, "textAlignment", TextAlignment.LEFT);; 219 220 /** 221 * Specifies the behavior to use if the text of the {@code Tooltip} 222 * exceeds the available space for rendering the text. 223 */ 224 public final ObjectProperty<OverrunStyle> textOverrunProperty() { 225 return textOverrun; 226 } 227 public final void setTextOverrun(OverrunStyle value) { 228 textOverrunProperty().setValue(value); 229 } 230 public final OverrunStyle getTextOverrun() { 231 return textOverrunProperty().getValue(); 232 } 233 private final ObjectProperty<OverrunStyle> textOverrun = 234 new SimpleStyleableObjectProperty<OverrunStyle>(TEXT_OVERRUN, this, "textOverrun", OverrunStyle.ELLIPSIS); 235 236 /** 237 * If a run of text exceeds the width of the Tooltip, then this variable 238 * indicates whether the text should wrap onto another line. 239 */ 240 public final BooleanProperty wrapTextProperty() { 241 return wrapText; 242 } 243 public final void setWrapText(boolean value) { 244 wrapTextProperty().setValue(value); 245 } 246 public final boolean isWrapText() { 247 return wrapTextProperty().getValue(); 248 } 249 private final BooleanProperty wrapText = 250 new SimpleStyleableBooleanProperty(WRAP_TEXT, this, "wrapText", false); 251 252 253 /** 254 * The default font to use for text in the Tooltip. If the Tooltip's text is 255 * rich text then this font may or may not be used depending on the font 256 * information embedded in the rich text, but in any case where a default 257 * font is required, this font will be used. 258 */ 259 public final ObjectProperty<Font> fontProperty() { 260 return font; 261 } 262 public final void setFont(Font value) { 263 fontProperty().setValue(value); 264 } 265 public final Font getFont() { 266 return fontProperty().getValue(); 267 } 268 private final ObjectProperty<Font> font = new StyleableObjectProperty<Font>(Font.getDefault()) { 269 private boolean fontSetByCss = false; 270 271 @Override public void applyStyle(StyleOrigin newOrigin, Font value) { 272 // RT-20727 - if CSS is setting the font, then make sure invalidate doesn't call NodeHelper.reapplyCSS 273 try { 274 // super.applyStyle calls set which might throw if value is bound. 275 // Have to make sure fontSetByCss is reset. 276 fontSetByCss = true; 277 super.applyStyle(newOrigin, value); 300 } 301 302 @Override public CssMetaData<Tooltip.CSSBridge,Font> getCssMetaData() { 303 return FONT; 304 } 305 306 @Override public Object getBean() { 307 return Tooltip.this; 308 } 309 310 @Override public String getName() { 311 return "font"; 312 } 313 }; 314 315 316 /** 317 * The delay between the mouse entering the hovered node and when the associated tooltip will be shown to the user. 318 * The default delay is 1000ms. 319 * 320 * @since 9 321 * @defaultvalue 1000ms 322 */ 323 public final ObjectProperty<Duration> showDelayProperty() { 324 return showDelayProperty; 325 } 326 public final void setShowDelay(Duration showDelay) { 327 showDelayProperty.set(showDelay); 328 } 329 public final Duration getShowDelay() { 330 return showDelayProperty.get(); 331 } 332 private final ObjectProperty<Duration> showDelayProperty 333 = new SimpleStyleableObjectProperty<>(SHOW_DELAY, this, "showDelay", new Duration(1000)); 334 335 336 /** 337 * The duration that the tooltip should remain showing for until it is no longer visible to the user. 338 * If the mouse leaves the control before the showDuration finishes, then the tooltip will remain showing 339 * for the duration specified in the {@link #hideDelayProperty()}, even if the remaining time of the showDuration 340 * is less than the hideDelay duration. The default value is 5000ms. 341 * 342 * @since 9 343 * @defaultvalue 5000ms 344 */ 345 public final ObjectProperty<Duration> showDurationProperty() { 346 return showDurationProperty; 347 } 348 public final void setShowDuration(Duration showDuration) { 349 showDurationProperty.set(showDuration); 350 } 351 public final Duration getShowDuration() { 352 return showDurationProperty.get(); 353 } 354 private final ObjectProperty<Duration> showDurationProperty 355 = new SimpleStyleableObjectProperty<>(SHOW_DURATION, this, "showDuration", new Duration(5000)); 356 357 358 /** 359 * The duration in which to continue showing the tooltip after the mouse has left the node. Once this time has 360 * elapsed the tooltip will hide. The default value is 200ms. 361 * 362 * @since 9 363 * @defaultvalue 200ms 364 */ 365 public final ObjectProperty<Duration> hideDelayProperty() { 366 return hideDelayProperty; 367 } 368 public final void setHideDelay(Duration hideDelay) { 369 hideDelayProperty.set(hideDelay); 370 } 371 public final Duration getHideDelay() { 372 return hideDelayProperty.get(); 373 } 374 private final ObjectProperty<Duration> hideDelayProperty 375 = new SimpleStyleableObjectProperty<>(HIDE_DELAY, this, "hideDelay", new Duration(200)); 376 377 378 /** 379 * An optional icon for the Tooltip. This can be positioned relative to the 380 * text by using the {@link #contentDisplayProperty() content display} 381 * property. 382 * The node specified for this variable cannot appear elsewhere in the 383 * scene graph, otherwise the {@code IllegalArgumentException} is thrown. 384 * See the class description of {@link javafx.scene.Node Node} for more detail. 385 */ 386 public final ObjectProperty<Node> graphicProperty() { 387 return graphic; 388 } 389 public final void setGraphic(Node value) { 390 graphicProperty().setValue(value); 391 } 392 public final Node getGraphic() { 393 return graphicProperty().getValue(); 394 } 395 private final ObjectProperty<Node> graphic = new StyleableObjectProperty<Node>() { 396 // The graphic is styleable by css, but it is the 397 // imageUrlProperty that handles the style value. 398 @Override public CssMetaData getCssMetaData() { 399 return GRAPHIC; 400 } 401 402 @Override public Object getBean() { 403 return Tooltip.this; 404 } 493 @Override public Object getBean() { 494 return Tooltip.this; 495 } 496 497 @Override public String getName() { 498 return "imageUrl"; 499 } 500 501 @Override public CssMetaData<Tooltip.CSSBridge,String> getCssMetaData() { 502 return GRAPHIC; 503 } 504 }; 505 } 506 return imageUrl; 507 } 508 509 private StyleableStringProperty imageUrl = null; 510 511 /** 512 * Specifies the positioning of the graphic relative to the text. 513 */ 514 public final ObjectProperty<ContentDisplay> contentDisplayProperty() { 515 return contentDisplay; 516 } 517 public final void setContentDisplay(ContentDisplay value) { 518 contentDisplayProperty().setValue(value); 519 } 520 public final ContentDisplay getContentDisplay() { 521 return contentDisplayProperty().getValue(); 522 } 523 private final ObjectProperty<ContentDisplay> contentDisplay = 524 new SimpleStyleableObjectProperty<>(CONTENT_DISPLAY, this, "contentDisplay", ContentDisplay.LEFT); 525 526 /** 527 * The amount of space between the graphic and text 528 */ 529 public final DoubleProperty graphicTextGapProperty() { 530 return graphicTextGap; 531 } 532 public final void setGraphicTextGap(double value) { 533 graphicTextGapProperty().setValue(value); 534 } 535 public final double getGraphicTextGap() { 536 return graphicTextGapProperty().getValue(); 537 } 538 private final DoubleProperty graphicTextGap = 539 new SimpleStyleableDoubleProperty(GRAPHIC_TEXT_GAP, this, "graphicTextGap", 4d); 540 541 /** 542 * Typically, the tooltip is "activated" when the mouse moves over a Control. 543 * There is usually some delay between when the Tooltip becomes "activated" 544 * and when it is actually shown. The details (such as the amount of delay, etc) 545 * is left to the Skin implementation. 546 */ 547 private final ReadOnlyBooleanWrapper activated = new ReadOnlyBooleanWrapper(this, "activated"); | 113 */ 114 @IDProperty("id") 115 public class Tooltip extends PopupControl { 116 private static String TOOLTIP_PROP_KEY = "javafx.scene.control.Tooltip"; 117 118 // RT-31134 : the tooltip style includes a shadow around the tooltip with a 119 // width of 9 and height of 5. This causes mouse events to not reach the control 120 // underneath resulting in losing hover state on the control while the tooltip is showing. 121 // Displaying the tooltip at an offset indicated below resolves this issue. 122 // RT-37107: The y-offset was upped to 7 to ensure no overlaps when the tooltip 123 // is shown near the right edge of the screen. 124 private static int TOOLTIP_XOFFSET = 10; 125 private static int TOOLTIP_YOFFSET = 7; 126 127 private static TooltipBehavior BEHAVIOR = new TooltipBehavior(false); 128 129 /** 130 * Associates the given {@link Tooltip} with the given {@link Node}. The tooltip 131 * can then behave similar to when it is set on any {@link Control}. A single 132 * tooltip can be associated with multiple nodes. 133 * @param node the node 134 * @param t the tooltip 135 * @see Tooltip 136 */ 137 public static void install(Node node, Tooltip t) { 138 BEHAVIOR.install(node, t); 139 } 140 141 /** 142 * Removes the association of the given {@link Tooltip} on the specified 143 * {@link Node}. Hence hovering on the node will no longer result in showing of the 144 * tooltip. 145 * @param node the node 146 * @param t the tooltip 147 * @see Tooltip 148 */ 149 public static void uninstall(Node node, Tooltip t) { 150 BEHAVIOR.uninstall(node); 151 } 152 153 /*************************************************************************** 154 * * 155 * Constructors * 156 * * 157 **************************************************************************/ 158 159 /** 160 * Creates a tooltip with an empty string for its text. 161 */ 162 public Tooltip() { 163 this(null); 164 } 165 166 /** 167 * Creates a tooltip with the specified text. 168 * 169 * @param text A text string for the tooltip. 170 */ 171 public Tooltip(String text) { 172 super(); 173 if (text != null) setText(text); 174 bridge = new CSSBridge(); 175 PopupWindowHelper.getContent(this).setAll(bridge); 176 getStyleClass().setAll("tooltip"); 177 } 178 179 /*************************************************************************** 180 * * 181 * Properties * 182 * * 183 **************************************************************************/ 184 /** 185 * The text to display in the tooltip. If the text is set to null, an empty 186 * string will be displayed, despite the value being null. 187 * @return the text property 188 */ 189 public final StringProperty textProperty() { return text; } 190 public final void setText(String value) { 191 textProperty().setValue(value); 192 } 193 public final String getText() { return text.getValue() == null ? "" : text.getValue(); } 194 private final StringProperty text = new SimpleStringProperty(this, "text", "") { 195 @Override protected void invalidated() { 196 super.invalidated(); 197 final String value = get(); 198 if (isShowing() && value != null && !value.equals(getText())) { 199 //Dynamic tooltip content is location-dependant. 200 //Chromium trick. 201 setAnchorX(BEHAVIOR.lastMouseX); 202 setAnchorY(BEHAVIOR.lastMouseY); 203 } 204 } 205 }; 206 207 /** 208 * Specifies the behavior for lines of text <em>when text is multiline</em>. 209 * Unlike {@link #contentDisplayProperty() contentDisplay} which affects the 210 * graphic and text, this setting only affects multiple lines of text 211 * relative to the text bounds. 212 * @return the text alignment property 213 */ 214 public final ObjectProperty<TextAlignment> textAlignmentProperty() { 215 return textAlignment; 216 } 217 public final void setTextAlignment(TextAlignment value) { 218 textAlignmentProperty().setValue(value); 219 } 220 public final TextAlignment getTextAlignment() { 221 return textAlignmentProperty().getValue(); 222 } 223 private final ObjectProperty<TextAlignment> textAlignment = 224 new SimpleStyleableObjectProperty<>(TEXT_ALIGNMENT, this, "textAlignment", TextAlignment.LEFT);; 225 226 /** 227 * Specifies the behavior to use if the text of the {@code Tooltip} 228 * exceeds the available space for rendering the text. 229 * @return the text overrun property 230 */ 231 public final ObjectProperty<OverrunStyle> textOverrunProperty() { 232 return textOverrun; 233 } 234 public final void setTextOverrun(OverrunStyle value) { 235 textOverrunProperty().setValue(value); 236 } 237 public final OverrunStyle getTextOverrun() { 238 return textOverrunProperty().getValue(); 239 } 240 private final ObjectProperty<OverrunStyle> textOverrun = 241 new SimpleStyleableObjectProperty<OverrunStyle>(TEXT_OVERRUN, this, "textOverrun", OverrunStyle.ELLIPSIS); 242 243 /** 244 * If a run of text exceeds the width of the Tooltip, then this variable 245 * indicates whether the text should wrap onto another line. 246 * @return the wrap text property 247 */ 248 public final BooleanProperty wrapTextProperty() { 249 return wrapText; 250 } 251 public final void setWrapText(boolean value) { 252 wrapTextProperty().setValue(value); 253 } 254 public final boolean isWrapText() { 255 return wrapTextProperty().getValue(); 256 } 257 private final BooleanProperty wrapText = 258 new SimpleStyleableBooleanProperty(WRAP_TEXT, this, "wrapText", false); 259 260 261 /** 262 * The default font to use for text in the Tooltip. If the Tooltip's text is 263 * rich text then this font may or may not be used depending on the font 264 * information embedded in the rich text, but in any case where a default 265 * font is required, this font will be used. 266 * @return the font property 267 */ 268 public final ObjectProperty<Font> fontProperty() { 269 return font; 270 } 271 public final void setFont(Font value) { 272 fontProperty().setValue(value); 273 } 274 public final Font getFont() { 275 return fontProperty().getValue(); 276 } 277 private final ObjectProperty<Font> font = new StyleableObjectProperty<Font>(Font.getDefault()) { 278 private boolean fontSetByCss = false; 279 280 @Override public void applyStyle(StyleOrigin newOrigin, Font value) { 281 // RT-20727 - if CSS is setting the font, then make sure invalidate doesn't call NodeHelper.reapplyCSS 282 try { 283 // super.applyStyle calls set which might throw if value is bound. 284 // Have to make sure fontSetByCss is reset. 285 fontSetByCss = true; 286 super.applyStyle(newOrigin, value); 309 } 310 311 @Override public CssMetaData<Tooltip.CSSBridge,Font> getCssMetaData() { 312 return FONT; 313 } 314 315 @Override public Object getBean() { 316 return Tooltip.this; 317 } 318 319 @Override public String getName() { 320 return "font"; 321 } 322 }; 323 324 325 /** 326 * The delay between the mouse entering the hovered node and when the associated tooltip will be shown to the user. 327 * The default delay is 1000ms. 328 * 329 * @return show delay property 330 * @since 9 331 * @defaultValue 1000ms 332 */ 333 public final ObjectProperty<Duration> showDelayProperty() { 334 return showDelayProperty; 335 } 336 public final void setShowDelay(Duration showDelay) { 337 showDelayProperty.set(showDelay); 338 } 339 public final Duration getShowDelay() { 340 return showDelayProperty.get(); 341 } 342 private final ObjectProperty<Duration> showDelayProperty 343 = new SimpleStyleableObjectProperty<>(SHOW_DELAY, this, "showDelay", new Duration(1000)); 344 345 346 /** 347 * The duration that the tooltip should remain showing for until it is no longer visible to the user. 348 * If the mouse leaves the control before the showDuration finishes, then the tooltip will remain showing 349 * for the duration specified in the {@link #hideDelayProperty()}, even if the remaining time of the showDuration 350 * is less than the hideDelay duration. The default value is 5000ms. 351 * 352 * @return the show duration property 353 * @since 9 354 * @defaultValue 5000ms 355 */ 356 public final ObjectProperty<Duration> showDurationProperty() { 357 return showDurationProperty; 358 } 359 public final void setShowDuration(Duration showDuration) { 360 showDurationProperty.set(showDuration); 361 } 362 public final Duration getShowDuration() { 363 return showDurationProperty.get(); 364 } 365 private final ObjectProperty<Duration> showDurationProperty 366 = new SimpleStyleableObjectProperty<>(SHOW_DURATION, this, "showDuration", new Duration(5000)); 367 368 369 /** 370 * The duration in which to continue showing the tooltip after the mouse has left the node. Once this time has 371 * elapsed the tooltip will hide. The default value is 200ms. 372 * 373 * @return the hide delay property 374 * @since 9 375 * @defaultValue 200ms 376 */ 377 public final ObjectProperty<Duration> hideDelayProperty() { 378 return hideDelayProperty; 379 } 380 public final void setHideDelay(Duration hideDelay) { 381 hideDelayProperty.set(hideDelay); 382 } 383 public final Duration getHideDelay() { 384 return hideDelayProperty.get(); 385 } 386 private final ObjectProperty<Duration> hideDelayProperty 387 = new SimpleStyleableObjectProperty<>(HIDE_DELAY, this, "hideDelay", new Duration(200)); 388 389 390 /** 391 * An optional icon for the Tooltip. This can be positioned relative to the 392 * text by using the {@link #contentDisplayProperty() content display} 393 * property. 394 * The node specified for this variable cannot appear elsewhere in the 395 * scene graph, otherwise the {@code IllegalArgumentException} is thrown. 396 * See the class description of {@link javafx.scene.Node Node} for more detail. 397 * @return the graphic property 398 */ 399 public final ObjectProperty<Node> graphicProperty() { 400 return graphic; 401 } 402 public final void setGraphic(Node value) { 403 graphicProperty().setValue(value); 404 } 405 public final Node getGraphic() { 406 return graphicProperty().getValue(); 407 } 408 private final ObjectProperty<Node> graphic = new StyleableObjectProperty<Node>() { 409 // The graphic is styleable by css, but it is the 410 // imageUrlProperty that handles the style value. 411 @Override public CssMetaData getCssMetaData() { 412 return GRAPHIC; 413 } 414 415 @Override public Object getBean() { 416 return Tooltip.this; 417 } 506 @Override public Object getBean() { 507 return Tooltip.this; 508 } 509 510 @Override public String getName() { 511 return "imageUrl"; 512 } 513 514 @Override public CssMetaData<Tooltip.CSSBridge,String> getCssMetaData() { 515 return GRAPHIC; 516 } 517 }; 518 } 519 return imageUrl; 520 } 521 522 private StyleableStringProperty imageUrl = null; 523 524 /** 525 * Specifies the positioning of the graphic relative to the text. 526 * @return the content display property 527 */ 528 public final ObjectProperty<ContentDisplay> contentDisplayProperty() { 529 return contentDisplay; 530 } 531 public final void setContentDisplay(ContentDisplay value) { 532 contentDisplayProperty().setValue(value); 533 } 534 public final ContentDisplay getContentDisplay() { 535 return contentDisplayProperty().getValue(); 536 } 537 private final ObjectProperty<ContentDisplay> contentDisplay = 538 new SimpleStyleableObjectProperty<>(CONTENT_DISPLAY, this, "contentDisplay", ContentDisplay.LEFT); 539 540 /** 541 * The amount of space between the graphic and text 542 * @return the graphic text gap property 543 */ 544 public final DoubleProperty graphicTextGapProperty() { 545 return graphicTextGap; 546 } 547 public final void setGraphicTextGap(double value) { 548 graphicTextGapProperty().setValue(value); 549 } 550 public final double getGraphicTextGap() { 551 return graphicTextGapProperty().getValue(); 552 } 553 private final DoubleProperty graphicTextGap = 554 new SimpleStyleableDoubleProperty(GRAPHIC_TEXT_GAP, this, "graphicTextGap", 4d); 555 556 /** 557 * Typically, the tooltip is "activated" when the mouse moves over a Control. 558 * There is usually some delay between when the Tooltip becomes "activated" 559 * and when it is actually shown. The details (such as the amount of delay, etc) 560 * is left to the Skin implementation. 561 */ 562 private final ReadOnlyBooleanWrapper activated = new ReadOnlyBooleanWrapper(this, "activated"); |