diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d16d8ba..188d8958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ FlatLaf Change Log overwrite core values in addons. Also, addon loading order can be specified. - TableHeader: Paint column borders if renderer has changed, but delegates to the system default renderer (e.g. done in NetBeans). - +- Label and ToolTip: Fixed font sizes for HTML headings. ## 0.23.1 diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java index 66779db7..6ed86360 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatLabelUI.java @@ -20,12 +20,14 @@ import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicLabelUI; import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.util.UIScale; @@ -77,6 +79,51 @@ public class FlatLabelUI defaults_initialized = false; } + @Override + protected void installComponents( JLabel c ) { + super.installComponents( c ); + + // update HTML renderer if necessary + updateHTMLRenderer( c, c.getText(), false ); + } + + @Override + public void propertyChange( PropertyChangeEvent e ) { + String name = e.getPropertyName(); + if( name == "text" || name == "font" || name == "foreground" ) { + JLabel label = (JLabel) e.getSource(); + updateHTMLRenderer( label, label.getText(), true ); + } else + super.propertyChange( e ); + } + + /** + * Checks whether text contains HTML headings and adds a special CSS rule to + * re-calculate heading font sizes based on current component font size. + */ + static void updateHTMLRenderer( JComponent c, String text, boolean always ) { + if( BasicHTML.isHTMLString( text ) && + c.getClientProperty( "html.disable" ) != Boolean.TRUE && + text.contains( "" ); + + String style = ""; + if( headIndex < 0 ) + style = "" + style + ""; + + int insertIndex = headIndex >= 0 ? (headIndex + "".length()) : "".length(); + text = text.substring( 0, insertIndex ) + + style + + text.substring( insertIndex ); + } else if( !always ) + return; // not necessary to invoke BasicHTML.updateRenderer() + + BasicHTML.updateRenderer( c, text ); + } + @Override protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) { int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1; diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolTipUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolTipUI.java index ad3b501e..ee42b6e8 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolTipUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatToolTipUI.java @@ -21,6 +21,7 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; +import java.beans.PropertyChangeListener; import java.util.List; import javax.swing.JComponent; import javax.swing.JToolTip; @@ -47,6 +48,8 @@ import com.formdev.flatlaf.util.StringUtils; public class FlatToolTipUI extends BasicToolTipUI { + private static PropertyChangeListener sharedPropertyChangedListener; + private static ComponentUI instance; public static ComponentUI createUI( JComponent c ) { @@ -55,6 +58,38 @@ public class FlatToolTipUI return instance; } + @Override + public void installUI( JComponent c ) { + super.installUI( c ); + + // update HTML renderer if necessary + FlatLabelUI.updateHTMLRenderer( c, ((JToolTip)c).getTipText(), false ); + } + + @Override + protected void installListeners( JComponent c ) { + super.installListeners( c ); + + if( sharedPropertyChangedListener == null ) { + sharedPropertyChangedListener = e -> { + String name = e.getPropertyName(); + if( name == "text" || name == "font" || name == "foreground" ) { + JToolTip toolTip = (JToolTip) e.getSource(); + FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false ); + } + }; + } + + c.addPropertyChangeListener( sharedPropertyChangedListener ); + } + + @Override + protected void uninstallListeners( JComponent c ) { + super.uninstallListeners( c ); + + c.removePropertyChangeListener( sharedPropertyChangedListener ); + } + @Override public Dimension getPreferredSize( JComponent c ) { if( isMultiLine( c ) ) { diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java index 89eb57d7..90c710e6 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.java @@ -162,14 +162,7 @@ public class FlatComponentsTest JToggleButton toggleButton7 = new JToggleButton(); JLabel scrollBarLabel = new JLabel(); JScrollBar scrollBar1 = new JScrollBar(); - JLabel label4 = new JLabel(); JScrollBar scrollBar4 = new JScrollBar(); - JPanel panel3 = new JPanel(); - JLabel label3 = new JLabel(); - JScrollPane scrollPane15 = new JScrollPane(); - JEditorPane editorPane6 = new JEditorPane(); - JScrollPane scrollPane16 = new JScrollPane(); - JTextPane textPane6 = new JTextPane(); JScrollBar scrollBar5 = new JScrollBar(); JScrollBar scrollBar6 = new JScrollBar(); JLabel separatorLabel = new JLabel(); @@ -806,53 +799,11 @@ public class FlatComponentsTest scrollBar1.setOrientation(Adjustable.HORIZONTAL); add(scrollBar1, "cell 1 14,growx"); - //---- label4 ---- - label4.setText("HTML:"); - add(label4, "cell 5 14"); - //---- scrollBar4 ---- scrollBar4.setOrientation(Adjustable.HORIZONTAL); scrollBar4.setEnabled(false); add(scrollBar4, "cell 1 15,growx"); - //======== panel3 ======== - { - panel3.setOpaque(false); - panel3.setLayout(new MigLayout( - "ltr,insets 0,hidemode 3", - // columns - "[]", - // rows - "[]" + - "[]" + - "[]")); - - //---- label3 ---- - label3.setText("JLabel HTML
Sample content
text with link"); - panel3.add(label3, "cell 0 0"); - - //======== scrollPane15 ======== - { - - //---- editorPane6 ---- - editorPane6.setContentType("text/html"); - editorPane6.setText("JEditorPane HTML
Sample content
text with link"); - scrollPane15.setViewportView(editorPane6); - } - panel3.add(scrollPane15, "cell 0 1,grow"); - - //======== scrollPane16 ======== - { - - //---- textPane6 ---- - textPane6.setContentType("text/html"); - textPane6.setText("JTextPane HTML
Sample content
text with link"); - scrollPane16.setViewportView(textPane6); - } - panel3.add(scrollPane16, "cell 0 2,grow"); - } - add(panel3, "cell 5 15 1 9,aligny top,grow 100 0"); - //---- scrollBar5 ---- scrollBar5.setOrientation(Adjustable.HORIZONTAL); scrollBar5.putClientProperty("JScrollBar.showButtons", true); diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd index 9cc96640..0ab48b7e 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponentsTest.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.0.0.194" Java: "11.0.2" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.0.0.194" Java: "13.0.1" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -756,12 +756,6 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 14,growx" } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "label4" - "text": "HTML:" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 14" - } ) add( new FormComponent( "javax.swing.JScrollBar" ) { name: "scrollBar4" "orientation": 0 @@ -769,42 +763,6 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 15,growx" } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$columnConstraints": "[]" - "$rowConstraints": "[][][]" - "$layoutConstraints": "ltr,insets 0,hidemode 3" - } ) { - name: "panel3" - "opaque": false - add( new FormComponent( "javax.swing.JLabel" ) { - name: "label3" - "text": "JLabel HTML
Sample content
text with link" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "scrollPane15" - add( new FormComponent( "javax.swing.JEditorPane" ) { - name: "editorPane6" - "contentType": "text/html" - "text": "JEditorPane HTML
Sample content
text with link" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1,grow" - } ) - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "scrollPane16" - add( new FormComponent( "javax.swing.JTextPane" ) { - name: "textPane6" - "contentType": "text/html" - "text": "JTextPane HTML
Sample content
text with link" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2,grow" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 15 1 9,aligny top,grow 100 0" - } ) add( new FormComponent( "javax.swing.JScrollBar" ) { name: "scrollBar5" "orientation": 0 @@ -977,7 +935,7 @@ new FormModel { } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) - "size": new java.awt.Dimension( 865, 750 ) + "size": new java.awt.Dimension( 865, 800 ) } ) } } diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatHtmlTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatHtmlTest.java new file mode 100644 index 00000000..cfd48649 --- /dev/null +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatHtmlTest.java @@ -0,0 +1,186 @@ +/* + * Copyright 2020 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.testing; + +import java.awt.*; +import javax.swing.*; +import com.formdev.flatlaf.util.UIScale; +import net.miginfocom.swing.*; + +/** + * @author Karl Tauber + */ +public class FlatHtmlTest + extends FlatTestPanel +{ + public static void main( String[] args ) { + SwingUtilities.invokeLater( () -> { + FlatTestFrame frame = FlatTestFrame.create( args, "FlatHtmlTest" ); + frame.showFrame( FlatHtmlTest::new ); + } ); + } + + FlatHtmlTest() { + initComponents(); + increaseFontSize(); + } + + private void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents + labelLabel = new JLabel(); + editorPaneLabel = new JLabel(); + textPaneLabel = new JLabel(); + toolTipLabel = new JLabel(); + label1 = new JLabel(); + scrollPane15 = new JScrollPane(); + editorPane1 = new JEditorPane(); + scrollPane16 = new JScrollPane(); + textPane1 = new JTextPane(); + toolTip1 = new JToolTip(); + label2 = new JLabel(); + scrollPane17 = new JScrollPane(); + editorPane2 = new JEditorPane(); + scrollPane18 = new JScrollPane(); + textPane2 = new JTextPane(); + toolTip2 = new JToolTip(); + + //======== this ======== + setLayout(new MigLayout( + "ltr,insets dialog,hidemode 3", + // columns + "[fill]" + + "[fill]" + + "[fill]" + + "[fill]", + // rows + "[]" + + "[top]" + + "[top]")); + + //---- labelLabel ---- + labelLabel.setText("JLabel:"); + add(labelLabel, "cell 0 0"); + + //---- editorPaneLabel ---- + editorPaneLabel.setText("JEditorPane:"); + add(editorPaneLabel, "cell 1 0"); + + //---- textPaneLabel ---- + textPaneLabel.setText("JTextPane:"); + add(textPaneLabel, "cell 2 0"); + + //---- toolTipLabel ---- + toolTipLabel.setText("JToolTip:"); + add(toolTipLabel, "cell 3 0"); + + //---- label1 ---- + label1.setText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + add(label1, "cell 0 1"); + + //======== scrollPane15 ======== + { + + //---- editorPane1 ---- + editorPane1.setContentType("text/html"); + editorPane1.setText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + scrollPane15.setViewportView(editorPane1); + } + add(scrollPane15, "cell 1 1,grow"); + + //======== scrollPane16 ======== + { + + //---- textPane1 ---- + textPane1.setContentType("text/html"); + textPane1.setText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + scrollPane16.setViewportView(textPane1); + } + add(scrollPane16, "cell 2 1"); + + //---- toolTip1 ---- + toolTip1.setTipText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + add(toolTip1, "cell 3 1"); + + //---- label2 ---- + label2.setText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + add(label2, "cell 0 2"); + + //======== scrollPane17 ======== + { + + //---- editorPane2 ---- + editorPane2.setContentType("text/html"); + editorPane2.setText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + scrollPane17.setViewportView(editorPane2); + } + add(scrollPane17, "cell 1 2,grow"); + + //======== scrollPane18 ======== + { + + //---- textPane2 ---- + textPane2.setContentType("text/html"); + textPane2.setText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + scrollPane18.setViewportView(textPane2); + } + add(scrollPane18, "cell 2 2"); + + //---- toolTip2 ---- + toolTip2.setTipText("HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

"); + add(toolTip2, "cell 3 2"); + // JFormDesigner - End of component initialization //GEN-END:initComponents + } + + @Override + public void updateUI() { + super.updateUI(); + + EventQueue.invokeLater( () -> { + increaseFontSize(); + } ); + } + + private void increaseFontSize() { + increaseFontSize( label2, label1.getFont() ); + increaseFontSize( editorPane2, editorPane1.getFont() ); + increaseFontSize( textPane2, textPane1.getFont() ); + increaseFontSize( toolTip2, toolTip1.getFont() ); + } + + private void increaseFontSize( JComponent c, Font baseFont ) { + c.setFont( baseFont.deriveFont( Font.PLAIN, baseFont.getSize() + UIScale.scale( 10f ) ) ); + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables + private JLabel labelLabel; + private JLabel editorPaneLabel; + private JLabel textPaneLabel; + private JLabel toolTipLabel; + private JLabel label1; + private JScrollPane scrollPane15; + private JEditorPane editorPane1; + private JScrollPane scrollPane16; + private JTextPane textPane1; + private JToolTip toolTip1; + private JLabel label2; + private JScrollPane scrollPane17; + private JEditorPane editorPane2; + private JScrollPane scrollPane18; + private JTextPane textPane2; + private JToolTip toolTip2; + // JFormDesigner - End of variables declaration //GEN-END:variables +} diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatHtmlTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatHtmlTest.jfd new file mode 100644 index 00000000..df2c31ec --- /dev/null +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatHtmlTest.jfd @@ -0,0 +1,105 @@ +JFDML JFormDesigner: "7.0.0.0.194" Java: "13.0.1" encoding: "UTF-8" + +new FormModel { + contentType: "form/swing" + root: new FormRoot { + add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "ltr,insets dialog,hidemode 3" + "$columnConstraints": "[fill][fill][fill][fill]" + "$rowConstraints": "[][top][top]" + } ) { + name: "this" + add( new FormComponent( "javax.swing.JLabel" ) { + name: "labelLabel" + "text": "JLabel:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "editorPaneLabel" + "text": "JEditorPane:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "textPaneLabel" + "text": "JTextPane:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "toolTipLabel" + "text": "JToolTip:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label1" + "text": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane15" + add( new FormComponent( "javax.swing.JEditorPane" ) { + name: "editorPane1" + "contentType": "text/html" + "text": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 1,grow" + } ) + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane16" + add( new FormComponent( "javax.swing.JTextPane" ) { + name: "textPane1" + "contentType": "text/html" + "text": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 1" + } ) + add( new FormComponent( "javax.swing.JToolTip" ) { + name: "toolTip1" + "tipText": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 1" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label2" + "text": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane17" + add( new FormComponent( "javax.swing.JEditorPane" ) { + name: "editorPane2" + "contentType": "text/html" + "text": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 2,grow" + } ) + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane18" + add( new FormComponent( "javax.swing.JTextPane" ) { + name: "textPane2" + "contentType": "text/html" + "text": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 2" + } ) + add( new FormComponent( "javax.swing.JToolTip" ) { + name: "toolTip2" + "tipText": "HTML
Sample content
text with link

Header 1

Header 2

Header 3

Header 4

Header 5
Header 6

Paragraph

" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 2" + } ) + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 0 ) + "size": new java.awt.Dimension( 695, 755 ) + } ) + } +}