--- old/modules/javafx.controls/src/main/java/javafx/scene/control/skin/LabeledSkinBase.java 2017-10-17 16:47:02.304027999 +0530 +++ new/modules/javafx.controls/src/main/java/javafx/scene/control/skin/LabeledSkinBase.java 2017-10-17 16:47:02.083918000 +0530 @@ -25,6 +25,7 @@ package javafx.scene.control.skin; +import javafx.geometry.Point2D; import com.sun.javafx.scene.control.LabeledText; import com.sun.javafx.scene.control.behavior.TextBinding; import com.sun.javafx.scene.control.skin.Utils; @@ -573,13 +574,13 @@ contentY = (y + ((h - contentHeight) / 2.0)); } - double preMnemonicWidth = 0.0; + Point2D mnemonicPos = null; double mnemonicWidth = 0.0; double mnemonicHeight = 0.0; if (containsMnemonic) { final Font font = text.getFont(); String preSt = bindings.getText(); - preMnemonicWidth = Utils.computeTextWidth(font, preSt.substring(0, bindings.getMnemonicIndex()), 0); + mnemonicPos = Utils.computeMnemonicPosition(font, preSt, bindings.getMnemonicIndex(), this.wrapWidth, labeled.getLineSpacing()); mnemonicWidth = Utils.computeTextWidth(font, preSt.substring(bindings.getMnemonicIndex(), bindings.getMnemonicIndex() + 1), 0); mnemonicHeight = Utils.computeTextHeight(font, "_", 0, text.getBoundsType()); } @@ -612,9 +613,9 @@ // adjust the text based on the text's minX/minY so no need to // worry about that here text.relocate(snapPositionX(contentX), snapPositionY(contentY)); - if (containsMnemonic) { + if (containsMnemonic && (mnemonicPos != null)) { mnemonic_underscore.setEndX(mnemonicWidth-2.0); - mnemonic_underscore.relocate(contentX+preMnemonicWidth, contentY+mnemonicHeight-1); + mnemonic_underscore.relocate(contentX + mnemonicPos.getX(), contentY + mnemonicPos.getY()); } } else if (ignoreText) { @@ -623,11 +624,10 @@ // there is a graphic, the text isn't even in the scene) text.relocate(snapPositionX(contentX), snapPositionY(contentY)); graphic.relocate(snapPositionX(contentX), snapPositionY(contentY)); - if (containsMnemonic) { + if (containsMnemonic && (mnemonicPos != null)) { mnemonic_underscore.setEndX(mnemonicWidth); mnemonic_underscore.setStrokeWidth(mnemonicHeight/10.0); - mnemonic_underscore.relocate(contentX+preMnemonicWidth, contentY+mnemonicHeight-1); - + mnemonic_underscore.relocate(contentX + mnemonicPos.getX(), contentY + mnemonicPos.getY()); } } else { // There is both text and a graphic, so I need to position them @@ -670,10 +670,10 @@ textY = contentY + ((contentHeight - textHeight) / 2.0); } text.relocate(snapPositionX(textX), snapPositionY(textY)); - if (containsMnemonic) { + if (containsMnemonic && (mnemonicPos != null)) { mnemonic_underscore.setEndX(mnemonicWidth); mnemonic_underscore.setStrokeWidth(mnemonicHeight/10.0); - mnemonic_underscore.relocate(snapPositionX(textX+preMnemonicWidth), snapPositionY(textY+mnemonicHeight-1)); + mnemonic_underscore.relocate(textX + mnemonicPos.getX(), textY + mnemonicPos.getY()); } graphic.relocate(snapPositionX(graphicX), snapPositionY(graphicY)); } --- old/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/skin/Utils.java 2017-10-17 16:47:02.976364000 +0530 +++ new/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/skin/Utils.java 2017-10-17 16:47:02.756254000 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -146,6 +146,51 @@ return layout.getBounds().getHeight(); } + public static Point2D computeMnemonicPosition(Font font, String text, int mnemonicIndex, double wrappingWidth, + double lineSpacing) { + // Input validation + if ((font == null) || (text == null) || + (mnemonicIndex < 0) || (mnemonicIndex > text.length())) { + return null; + } + + // Layout the text with given font, wrapping width and line spacing + layout.setContent(text, FontHelper.getNativeFont(font)); + layout.setWrapWidth((float)wrappingWidth); + layout.setLineSpacing((float)lineSpacing); + + // The text could be spread over multiple lines + // We need to find out on which line the mnemonic character lies + int start = 0; + int i = 0; + int totalLines = layout.getLines().length; + while (i < totalLines) { + int lineLength = layout.getLines()[i].getLength(); + + if ((mnemonicIndex >= start) && + (mnemonicIndex < (start + lineLength))) { + // mnemonic lies on line 'i' + break; + } + + start = start + lineLength; + i++; + } + + // Find x and y offsets of mnemonic character position + // in line numbered 'i' + double lineHeight = layout.getBounds().getHeight() / totalLines; + double x = Utils.computeTextWidth(font, text.substring(start, mnemonicIndex), 0 ); + + double y = (lineHeight * (i+1)); + // Adjust y offset for linespacing except for the last line. + if ((i+1) != totalLines) { + y = y - (lineSpacing / 2); + } + + return new Point2D(x, y); + } + public static int computeTruncationIndex(Font font, String text, double width) { helper.setText(text); helper.setFont(font);