Skip to content
Snippets Groups Projects
Commit 6ad0187f authored by Raphael GIRARDOT's avatar Raphael GIRARDOT
Browse files

Better best font size calculation (JAVAAPI-644)

parent 30d2c46f
Branches
Tags
No related merge requests found
...@@ -280,8 +280,14 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -280,8 +280,14 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
if (frc != null) { if (frc != null) {
textBounds = font.getStringBounds(text, frc); textBounds = font.getStringBounds(text, frc);
} }
// Take account of maximum size only if programmatically set (JAVAAPI-644)
Dimension maxSize = (component == null) || (!component.isMaximumSizeSet()) ? null : component.getMaximumSize();
int maxWidth = maxSize == null ? 0 : maxSize.width;
if (borderAndIconMargin != null) {
maxWidth -= borderAndIconMargin.width;
}
return computeBestFontSizeIgnoreHeight(component, font, text, fixedTextWidth, compWidth, textBounds, return computeBestFontSizeIgnoreHeight(component, font, text, fixedTextWidth, compWidth, textBounds,
borderAndIconMargin); borderAndIconMargin, maxWidth);
} }
/** /**
...@@ -295,11 +301,12 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -295,11 +301,12 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
* @param textBounds The previously calculated text bounds. * @param textBounds The previously calculated text bounds.
* @param borderAndIconMargin The previously calculated border and icon margin (using * @param borderAndIconMargin The previously calculated border and icon margin (using
* {@link #getBorderAndIconMargin(JComponent)}). * {@link #getBorderAndIconMargin(JComponent)}).
* @param knownMaxWidth The known maximum width for the component (already reduced by border and icon margin)
* @return An <code>int</code>: the calculated best font size. <code>-1</code> if such a font size could not be * @return An <code>int</code>: the calculated best font size. <code>-1</code> if such a font size could not be
* calculated. You should avoid <code>0</code> too. * calculated. You should avoid <code>0</code> too.
*/ */
public static int computeBestFontSizeIgnoreHeight(Component component, Font font, String text, int fixedTextWidth, public static int computeBestFontSizeIgnoreHeight(Component component, Font font, String text, int fixedTextWidth,
int compWidth, Rectangle2D textBounds, Dimension borderAndIconMargin) { int compWidth, Rectangle2D textBounds, Dimension borderAndIconMargin, int knownMaxWidth) {
int fontSizeToUse; int fontSizeToUse;
if (text == null || text.isEmpty()) { if (text == null || text.isEmpty()) {
// No text found: no best font size. // No text found: no best font size.
...@@ -307,21 +314,45 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -307,21 +314,45 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
} else { } else {
// Step 2: Recover component and font sizes. // Step 2: Recover component and font sizes.
compWidth = getMaxAllowedWidth(component, borderAndIconMargin, compWidth); compWidth = getMaxAllowedWidth(component, borderAndIconMargin, compWidth);
int textWidth = fixedTextWidth < 1 ? getWidth(textBounds, component, font, text) : fixedTextWidth;
int textWidth;
if (fixedTextWidth < 1) {
textWidth = getWidth(textBounds, component, font, text);
} else {
textWidth = fixedTextWidth;
}
// Step 3: Find out how much the font can grow in width. // Step 3: Find out how much the font can grow in width.
float widthRatio = getBestRatio((float) compWidth / (float) textWidth); float widthRatio = getBestRatio((float) compWidth / (float) textWidth);
fontSizeToUse = (int) Math.floor(font.getSize() * widthRatio); fontSizeToUse = (int) Math.floor(font.getSize() * widthRatio);
// Step 4: Limit font size according to known maximum text width if it is > 0 (JAVAAPI-644)
if (knownMaxWidth > 0) {
int newWidth = textWidth * Math.round(fontSizeToUse / font.getSize2D());
if (newWidth > knownMaxWidth) {
widthRatio = getBestRatio(knownMaxWidth / (float) newWidth);
fontSizeToUse = Math.round(fontSizeToUse * widthRatio);
if (fontSizeToUse == 0) {
fontSizeToUse = -1;
}
}
}
} }
return fontSizeToUse; return fontSizeToUse;
} }
/**
* Computes the best {@link Font} size for a {@link Component}, according to given text.
*
* @param component The {@link Component}.
* @param font The referent {@link Font}.
* @param text The text.
* @param fixedTextWidth The previously calculated text width. Use a value &gt; 0 to avoid calculating text width.
* @param compWidth The previously calculated component width. Use a value &ge; 0 to avoid asking component's width.
* @param textBounds The previously calculated text bounds.
* @param borderAndIconMargin The previously calculated border and icon margin (using
* {@link #getBorderAndIconMargin(JComponent)}).
* @return An <code>int</code>: the calculated best font size. <code>-1</code> if such a font size could not be
* calculated. You should avoid <code>0</code> too.
*/
public static int computeBestFontSizeIgnoreHeight(Component component, Font font, String text, int fixedTextWidth,
int compWidth, Rectangle2D textBounds, Dimension borderAndIconMargin) {
return computeBestFontSizeIgnoreHeight(component, font, text, fixedTextWidth, compWidth, textBounds,
borderAndIconMargin, 0);
}
/** /**
* Adapts best {@link Font} size according to {@link Component} height. * Adapts best {@link Font} size according to {@link Component} height.
* *
...@@ -332,10 +363,12 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -332,10 +363,12 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
* @param compHeight The previously calculated maximum allowed text height. * @param compHeight The previously calculated maximum allowed text height.
* @param textBounds The previously calculated text bounds. * @param textBounds The previously calculated text bounds.
* @param fontSizeToUse The previously calculated best font size (ignoring height). * @param fontSizeToUse The previously calculated best font size (ignoring height).
* @param knownMaxHeight The known maximum height allowed for the {@link Component} (already reduced by border and
* icon margin).
* @return An <code>int</code>: the readapted best font size. * @return An <code>int</code>: the readapted best font size.
*/ */
protected static int computeBestFontSizeAccordingHeight(Font font, FontRenderContext frc, String text, protected static int computeBestFontSizeAccordingHeight(Font font, FontRenderContext frc, String text,
Component component, int compHeight, Rectangle2D textBounds, int fontSizeToUse) { Component component, int compHeight, Rectangle2D textBounds, int fontSizeToUse, int knownMaxHeight) {
float currentFontHeight; float currentFontHeight;
if ((text != null) && (!text.isEmpty()) && (frc != null) if ((text != null) && (!text.isEmpty()) && (frc != null)
&& ((component instanceof JLabel) || (component instanceof JTextComponent))) { && ((component instanceof JLabel) || (component instanceof JTextComponent))) {
...@@ -348,7 +381,36 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -348,7 +381,36 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
currentFontHeight = FontUtils.getFontHeight(font, frc, text, component, textBounds); currentFontHeight = FontUtils.getFontHeight(font, frc, text, component, textBounds);
} }
float heightRatio = getBestRatio(compHeight / currentFontHeight); float heightRatio = getBestRatio(compHeight / currentFontHeight);
return Math.min(fontSizeToUse, Math.round(font.getSize() * heightRatio)); int bestSize = Math.min(fontSizeToUse, Math.round(font.getSize() * heightRatio));
// Try to limit to maximum height if it is > 0 (JAVAAPI-644)
if (knownMaxHeight > 0) {
int newHeight = Math.round(currentFontHeight * (bestSize / font.getSize2D()));
if (newHeight > knownMaxHeight) {
heightRatio = getBestRatio(knownMaxHeight / (float) newHeight);
bestSize *= Math.round(bestSize * heightRatio);
if (bestSize == 0) {
bestSize = -1;
}
}
}
return bestSize;
}
/**
* Adapts best {@link Font} size according to {@link Component} height.
*
* @param font The {@link Font}.
* @param frc The {@link FontRenderContext}.
* @param text The known text.
* @param component The {@link Component}.
* @param compHeight The previously calculated maximum allowed text height.
* @param textBounds The previously calculated text bounds.
* @param fontSizeToUse The previously calculated best font size (ignoring height).
* @return An <code>int</code>: the readapted best font size.
*/
protected static int computeBestFontSizeAccordingHeight(Font font, FontRenderContext frc, String text,
Component component, int compHeight, Rectangle2D textBounds, int fontSizeToUse) {
return computeBestFontSizeAccordingHeight(font, frc, text, component, compHeight, textBounds, fontSizeToUse, 0);
} }
/** /**
...@@ -439,12 +501,37 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -439,12 +501,37 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
text = button.getText(); text = button.getText();
borderAndIconMargin = getBorderAndIconMargin(button); borderAndIconMargin = getBorderAndIconMargin(button);
} }
int knownMaxWidth, knownMaxHeight;
Dimension maxSize;
// Take account of maximum size only if programmatically set (JAVAAPI-644)
if ((component == null) || !component.isMaximumSizeSet()) {
maxSize = null;
} else {
maxSize = component.getMaximumSize();
}
if (maxSize == null) {
knownMaxWidth = knownMaxHeight = 0;
} else {
knownMaxWidth = maxSize.width;
knownMaxHeight = maxSize.height;
if (knownMaxWidth == Integer.MAX_VALUE) {
knownMaxWidth = 0;
}
if (knownMaxHeight == Integer.MAX_VALUE) {
knownMaxHeight = 0;
}
if (borderAndIconMargin != null) {
knownMaxWidth -= borderAndIconMargin.width;
knownMaxHeight -= borderAndIconMargin.height;
}
}
FontRenderContext frc = getFontRenderContext(component, text); FontRenderContext frc = getFontRenderContext(component, text);
if (frc != null) { if (frc != null) {
textBounds = font.getStringBounds(text, frc); textBounds = font.getStringBounds(text, frc);
} }
// Step 2 and Step 3: see computeBestFontSizeIgnoreHeight // Step 2 and Step 3: see computeBestFontSizeIgnoreHeight
fontSizeToUse = computeBestFontSizeIgnoreHeight(component, font, text, -1, -1, textBounds, borderAndIconMargin); fontSizeToUse = computeBestFontSizeIgnoreHeight(component, font, text, -1, -1, textBounds, borderAndIconMargin,
knownMaxWidth);
if (fontSizeToUse > -1) { if (fontSizeToUse > -1) {
int compHeight = component.getHeight(); int compHeight = component.getHeight();
if (borderAndIconMargin != null) { if (borderAndIconMargin != null) {
...@@ -453,7 +540,7 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -453,7 +540,7 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
// Step 4: Pick a new font size so it will not be larger than the height of label. // Step 4: Pick a new font size so it will not be larger than the height of label.
fontSizeToUse = computeBestFontSizeAccordingHeight(font, frc, text, component, compHeight, textBounds, fontSizeToUse = computeBestFontSizeAccordingHeight(font, frc, text, component, compHeight, textBounds,
fontSizeToUse); fontSizeToUse, knownMaxHeight);
// Check twice whether font size was misestimated. // Check twice whether font size was misestimated.
if (frc != null) { if (frc != null) {
int fWidth, fHeight; int fWidth, fHeight;
...@@ -479,35 +566,6 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -479,35 +566,6 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
} }
} }
} }
if ((component instanceof JComponent) && !(component instanceof IBestFontSizeCalculator)) {
JComponent comp = (JComponent) component;
Dimension maxSize = comp.getMaximumSize();
if ((frc != null) && (maxSize != null)
&& (maxSize.width < Integer.MAX_VALUE || maxSize.height < Integer.MAX_VALUE)) {
Dimension size;
int formerFontSize = 0;
while (fontSizeToUse > 1 && fontSizeToUse != formerFontSize) {
formerFontSize = fontSizeToUse;
size = getEstimatedSize(comp, text, font, fontSizeToUse, frc, borderAndIconMargin);
if ((size != null) && (maxSize.width < size.width || maxSize.height < size.height)) {
float ratio = 1;
if (maxSize.width < size.width) {
ratio = ((float) maxSize.width) / (float) size.width;
}
if (maxSize.height < size.height) {
ratio = Math.min(ratio, ((float) maxSize.height) / (float) size.height);
}
fontSizeToUse = Math.round(fontSizeToUse * getBestRatio(ratio));
if (fontSizeToUse < 1) {
fontSizeToUse = 1;
} else if (fontSizeToUse >= formerFontSize) {
fontSizeToUse--;
}
} // end if ((size != null) && (maxSize.width < size.width || maxSize.height < size.height))
} // end while (fontSizeToUse > 1 && fontSizeToUse != formerFontSize)
} // end if ((frc != null) && (maxSize != null)
// && (maxSize.width < Integer.MAX_VALUE || maxSize.height < Integer.MAX_VALUE))
} // end if ((component instanceof JComponent) && !(component instanceof IBestFontSizeCalculator))
return fontSizeToUse; return fontSizeToUse;
} }
...@@ -548,13 +606,14 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -548,13 +606,14 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
// and to limit the creation of runnables. // and to limit the creation of runnables.
Runnable runnable = COMPONENT_UPDATERS.get(component); Runnable runnable = COMPONENT_UPDATERS.get(component);
if (runnable == null) { if (runnable == null) {
// Can't use lambda expression due to the need to access this Runnable
runnable = new Runnable() { runnable = new Runnable() {
@Override @Override
public void run() { public void run() {
try {
updateComponentFont(component);
} finally {
IN_EDT.remove(this); IN_EDT.remove(this);
// Update component font only if it is showing (optimization discovered during JAVAAPI-644).
if (component.isShowing()) {
updateComponentFont(component);
} }
} }
}; };
...@@ -655,7 +714,7 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen ...@@ -655,7 +714,7 @@ public class DynamicFontSizeManager implements IComponentMultiListener, Componen
component.addPropertyChangeListener(this); component.addPropertyChangeListener(this);
component.addHierarchyBoundsListener(this); component.addHierarchyBoundsListener(this);
component.addHierarchyListener(this); component.addHierarchyListener(this);
if ((component instanceof JTextComponent)) { if (component instanceof JTextComponent) {
JTextComponent textComponent = (JTextComponent) component; JTextComponent textComponent = (JTextComponent) component;
rememberTextComponent(textComponent); rememberTextComponent(textComponent);
textComponent.getDocument().addDocumentListener(this); textComponent.getDocument().addDocumentListener(this);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment