diff --git a/component/src/main/java/org/pushingpixels/radiance/component/api/common/model/LabelPresentationModel.java b/component/src/main/java/org/pushingpixels/radiance/component/api/common/model/LabelPresentationModel.java index cb4f1adcc..0faf80b52 100644 --- a/component/src/main/java/org/pushingpixels/radiance/component/api/common/model/LabelPresentationModel.java +++ b/component/src/main/java/org/pushingpixels/radiance/component/api/common/model/LabelPresentationModel.java @@ -44,7 +44,6 @@ public class LabelPresentationModel implements ImmutablePresentationModel { private RadianceThemingSlices.IconFilterStrategy enabledIconFilterStrategy; private RadianceThemingSlices.IconFilterStrategy disabledIconFilterStrategy; private Font font; - private int textMaxLines; private HorizontalAlignment horizontalAlignment; private int iconTextGap; private float horizontalGapScaleFactor; @@ -73,10 +72,6 @@ public Font getFont() { return this.font; } - public int getTextMaxLines() { - return this.textMaxLines; - } - public HorizontalAlignment getHorizontalAlignment() { return this.horizontalAlignment; } @@ -106,8 +101,7 @@ public static class Builder { private RadianceThemingSlices.IconFilterStrategy disabledIconFilterStrategy = RadianceThemingSlices.IconFilterStrategy.THEMED_FOLLOW_COLOR_SCHEME; private Font font = null; - private int textMaxLines = Integer.MAX_VALUE; - private HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER; + private HorizontalAlignment horizontalAlignment = HorizontalAlignment.LEADING; private int iconTextGap = DEFAULT_ICON_TEXT_GAP; private float horizontalGapScaleFactor = 1.0f; private String singleLineDisplayPrototype = null; @@ -138,11 +132,6 @@ public Builder setFont(Font font) { return this; } - public Builder setTextMaxLines(int textMaxLines) { - this.textMaxLines = textMaxLines; - return this; - } - public Builder setHorizontalAlignment(HorizontalAlignment horizontalAlignment) { if (horizontalAlignment == null) { throw new IllegalArgumentException("Cannot pass null horizontal alignment"); @@ -173,7 +162,6 @@ public LabelPresentationModel build() { presentationModel.enabledIconFilterStrategy = this.enabledIconFilterStrategy; presentationModel.disabledIconFilterStrategy = this.disabledIconFilterStrategy; presentationModel.font = this.font; - presentationModel.textMaxLines = this.textMaxLines; presentationModel.horizontalAlignment = this.horizontalAlignment; presentationModel.iconTextGap = this.iconTextGap; presentationModel.horizontalGapScaleFactor = this.horizontalGapScaleFactor; diff --git a/component/src/main/java/org/pushingpixels/radiance/component/internal/theming/common/ui/RadianceExoLabelUI.java b/component/src/main/java/org/pushingpixels/radiance/component/internal/theming/common/ui/RadianceExoLabelUI.java index 04b31bb00..3320bbb90 100644 --- a/component/src/main/java/org/pushingpixels/radiance/component/internal/theming/common/ui/RadianceExoLabelUI.java +++ b/component/src/main/java/org/pushingpixels/radiance/component/internal/theming/common/ui/RadianceExoLabelUI.java @@ -34,14 +34,12 @@ import org.pushingpixels.radiance.component.api.common.JExoLabel; import org.pushingpixels.radiance.component.api.common.model.LabelContentModel; import org.pushingpixels.radiance.component.api.common.model.LabelPresentationModel; -import org.pushingpixels.radiance.component.api.common.popup.JPopupPanel; import org.pushingpixels.radiance.theming.api.ComponentState; import org.pushingpixels.radiance.theming.api.RadianceThemingCortex; import org.pushingpixels.radiance.theming.internal.utils.*; import javax.swing.*; import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; import javax.swing.plaf.ComponentUI; import java.awt.*; import java.beans.PropertyChangeListener; @@ -54,7 +52,6 @@ public class RadianceExoLabelUI extends ComponentUI { private RadianceIcon icon; private JLabel singleLineLabel; - private JTextArea multiLineLabel; private PropertyChangeListener propertyChangeListener; public static ComponentUI createUI(JComponent comp) { @@ -83,47 +80,18 @@ public void installUI(JComponent c) { presentationModel.getEnabledIconFilterStrategy(), presentationModel.getDisabledIconFilterStrategy()); - if (presentationModel.getTextMaxLines() == 1) { - this.singleLineLabel = new JLabel(contentModel.getText()); - this.singleLineLabel.setBorder(new EmptyBorder(0, 0, 0, 0)); - this.singleLineLabel.setFocusable(false); - if (presentationModel.getFont() != null) { - this.singleLineLabel.setFont(presentationModel.getFont()); - } - switch (presentationModel.getHorizontalAlignment()) { - case LEADING: - case FILL: - this.singleLineLabel.setHorizontalAlignment(SwingUtilities.LEADING); - break; - case CENTER: - this.singleLineLabel.setHorizontalAlignment(SwingUtilities.CENTER); - break; - case TRAILING: - this.singleLineLabel.setHorizontalAlignment(SwingUtilities.TRAILING); - break; - } - label.add(this.singleLineLabel); - } else { - this.multiLineLabel = new JTextArea(contentModel.getText()); - this.multiLineLabel.setRows(presentationModel.getTextMaxLines()); - this.multiLineLabel.setLineWrap(true); - this.multiLineLabel.setWrapStyleWord(true); - this.multiLineLabel.setEditable(false); - this.multiLineLabel.setFocusable(false); - if (presentationModel.getFont() != null) { - this.multiLineLabel.setFont(presentationModel.getFont()); - } - label.add(this.multiLineLabel); + this.singleLineLabel = new JLabel(contentModel.getText()); + this.singleLineLabel.setBorder(new EmptyBorder(0, 0, 0, 0)); + this.singleLineLabel.setFocusable(false); + if (presentationModel.getFont() != null) { + this.singleLineLabel.setFont(presentationModel.getFont()); } + label.add(this.singleLineLabel); this.propertyChangeListener = evt -> { if ("text".equals(evt.getPropertyName())) { String newText = (String) evt.getNewValue(); - if (presentationModel.getTextMaxLines() == 1) { - singleLineLabel.setText(newText); - } else { - multiLineLabel.setText(newText); - } + singleLineLabel.setText(newText); } }; contentModel.addPropertyChangeListener(this.propertyChangeListener); @@ -140,16 +108,7 @@ public void removeLayoutComponent(Component comp) { @Override public Dimension preferredLayoutSize(Container parent) { JExoLabel label = (JExoLabel) c; - - LabelContentModel contentModel = label.getProjection().getContentModel(); - LabelPresentationModel presentationModel = label.getProjection().getPresentationModel(); - - if (presentationModel.getTextMaxLines() == 1) { - return getSingleLinePreferredDimension(label); - } - - // TODO - support multiline mode - return new Dimension(0, 0); + return getSingleLinePreferredDimension(label); } @Override @@ -166,57 +125,55 @@ public void layoutContainer(Container parent) { int width = label.getWidth(); - if (presentationModel.getTextMaxLines() == 1) { - int shiftX = 0; - int preferredWidth = getSingleLinePreferredDimension(label).width; - if (preferredWidth < label.getWidth()) { - // We have more horizontal space than needed to display the content. - // Consult the horizontal alignment attribute of the command button to see - // how we should shift the content horizontally. - switch (presentationModel.getHorizontalAlignment()) { - case LEADING: - case FILL: - break; - case CENTER: - // shift everything to be centered horizontally - shiftX = (width - preferredWidth) / 2; - break; - case TRAILING: - // shift everything to the end of the button bounds - shiftX = width - preferredWidth; - } + int shiftX = 0; + int preferredWidth = getSingleLineNoPrototypePreferredDimension(label).width; + if (preferredWidth < label.getWidth()) { + // We have more horizontal space than needed to display the content. + // Consult the horizontal alignment attribute of the command button to see + // how we should shift the content horizontally. + switch (presentationModel.getHorizontalAlignment()) { + case LEADING: + case FILL: + break; + case CENTER: + // shift everything to be centered horizontally + shiftX = (width - preferredWidth) / 2; + break; + case TRAILING: + // shift everything to the end of the button bounds + shiftX = width - preferredWidth; } + } - int textLeft = 0; - int textTop = presentationModel.getContentPadding().top; - int textRight = 0; - int textBottom = label.getHeight() - presentationModel.getContentPadding().bottom; - - if (label.getComponentOrientation().isLeftToRight()) { - if (contentModel.getIconFactory() == null) { - textLeft = presentationModel.getContentPadding().left; - } else { - textLeft = presentationModel.getContentPadding().left + - presentationModel.getIconDimension().width + - (int) (presentationModel.getIconTextGap() * - presentationModel.getHorizontalGapScaleFactor()); - } + int textLeft = 0; + int textTop = presentationModel.getContentPadding().top; + int textRight = 0; + int textBottom = label.getHeight() - presentationModel.getContentPadding().bottom; + + if (label.getComponentOrientation().isLeftToRight()) { + if (contentModel.getIconFactory() == null) { + textLeft = presentationModel.getContentPadding().left; + } else { + textLeft = presentationModel.getContentPadding().left + + presentationModel.getIconDimension().width + + (int) (presentationModel.getIconTextGap() * + presentationModel.getHorizontalGapScaleFactor()); + } + textRight = width - presentationModel.getContentPadding().right; + } else { + textLeft = presentationModel.getContentPadding().left; + if (contentModel.getIconFactory() == null) { textRight = width - presentationModel.getContentPadding().right; } else { - textLeft = presentationModel.getContentPadding().left; - if (contentModel.getIconFactory() == null) { - textRight = width - presentationModel.getContentPadding().right; - } else { - textRight = width - presentationModel.getContentPadding().right - - presentationModel.getIconDimension().width - - (int) (presentationModel.getIconTextGap() * - presentationModel.getHorizontalGapScaleFactor()); - } + textRight = width - presentationModel.getContentPadding().right - + presentationModel.getIconDimension().width - + (int) (presentationModel.getIconTextGap() * + presentationModel.getHorizontalGapScaleFactor()); } - - label.getComponent(0).setBounds(shiftX + textLeft, textTop, - shiftX + textRight - textLeft, textBottom - textTop); } + + label.getComponent(0).setBounds(shiftX + textLeft, textTop, + shiftX + textRight - textLeft, textBottom - textTop); } }); } @@ -277,6 +234,43 @@ private static Dimension getSingleLinePreferredDimension(JExoLabel label) { return new Dimension(width, height); } + private static Dimension getSingleLineNoPrototypePreferredDimension(JExoLabel label) { + LabelContentModel contentModel = label.getProjection().getContentModel(); + LabelPresentationModel presentationModel = label.getProjection().getPresentationModel(); + + int width = presentationModel.getContentPadding().left + + presentationModel.getContentPadding().right; + int height = presentationModel.getContentPadding().top + + presentationModel.getContentPadding().bottom; + + int iconHeight = (contentModel.getIconFactory() != null) + ? presentationModel.getIconDimension().height + : 0; + int iconWidth = (contentModel.getIconFactory() != null) + ? presentationModel.getIconDimension().width + : 0; + + if (iconWidth > 0) { + width = width + iconWidth + + (int) (presentationModel.getIconTextGap() * + presentationModel.getHorizontalGapScaleFactor()); + } + + Font font = (presentationModel.getFont() != null) + ? presentationModel.getFont() + : label.getFont(); + Dimension textDimension = + RadianceMetricsUtilities.getLabelPreferredSingleLineDimension( + label, contentModel.getText(), font); + int textWidth = textDimension.width; + int textHeight = textDimension.height; + + width = width + textWidth; + height += Math.max(iconHeight, textHeight); + + return new Dimension(width, height); + } + @Override public void paint(Graphics g, JComponent c) { JExoLabel label = (JExoLabel) c; diff --git a/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/TestExoLabels.java b/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/TestExoLabels.java new file mode 100644 index 000000000..c2b2d7234 --- /dev/null +++ b/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/TestExoLabels.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2005-2023 Radiance Kirill Grouchnikov. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.pushingpixels.radiance.demo.component.common; + +import com.jgoodies.forms.builder.FormBuilder; +import com.jgoodies.forms.factories.Paddings; +import org.pushingpixels.radiance.component.api.common.CommandButtonPresentationState; +import org.pushingpixels.radiance.component.api.common.HorizontalAlignment; +import org.pushingpixels.radiance.component.api.common.model.Command; +import org.pushingpixels.radiance.component.api.common.model.CommandButtonPresentationModel; +import org.pushingpixels.radiance.component.api.common.model.LabelContentModel; +import org.pushingpixels.radiance.component.api.common.model.LabelPresentationModel; +import org.pushingpixels.radiance.component.api.common.projection.LabelProjection; +import org.pushingpixels.radiance.demo.component.svg.logo.RadianceLogo; +import org.pushingpixels.radiance.demo.component.svg.material.transcoded.info_black_24dp; +import org.pushingpixels.radiance.demo.component.svg.tango.transcoded.Edit_paste; +import org.pushingpixels.radiance.theming.api.ComponentState; +import org.pushingpixels.radiance.theming.api.RadianceThemingCortex; +import org.pushingpixels.radiance.theming.api.RadianceThemingSlices; +import org.pushingpixels.radiance.theming.api.skin.BusinessSkin; + +import javax.swing.*; +import java.awt.*; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.ResourceBundle; + +public class TestExoLabels extends JFrame { + ResourceBundle resourceBundle; + + Locale currLocale; + + private TestExoLabels() { + super("Exo labels"); + this.setIconImage(RadianceLogo.getLogoImage(this, + RadianceThemingCortex.GlobalScope.getCurrentSkin().getColorScheme( + RadianceThemingSlices.DecorationAreaType.PRIMARY_TITLE_PANE, + RadianceThemingSlices.ColorSchemeAssociationKind.FILL, + ComponentState.ENABLED))); + + this.setLayout(new BorderLayout()); + + currLocale = Locale.getDefault(); + resourceBundle = ResourceBundle + .getBundle("org.pushingpixels.radiance.demo.component.resource.Resources", currLocale); + + FormBuilder builder = FormBuilder.create(). + columns("right:pref, 10dlu, 120dlu"). + rows("p, $lg, p, $lg, p, $lg, p, $lg, p, $lg, p, $lg, p, $lg, p"). + padding(Paddings.DIALOG); + + Font defaultFont = RadianceThemingCortex.GlobalScope.getFontPolicy().getFontSet().getControlFont(); + + builder.add("Regular").xy(1, 1); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label").build(), + LabelPresentationModel.builder().build() + ).buildComponent()).xy(3, 1); + + builder.add("Bold").xy(1, 3); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label").build(), + LabelPresentationModel.builder().setFont(defaultFont.deriveFont(Font.BOLD)).build() + ).buildComponent()).xy(3, 3); + + builder.add("With icon").xy(1, 5); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label") + .setIconFactory(info_black_24dp.factory()).build(), + LabelPresentationModel.builder().build() + ).buildComponent()).xy(3, 5); + + builder.add("Bold with icon").xy(1, 7); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label") + .setIconFactory(info_black_24dp.factory()).build(), + LabelPresentationModel.builder().setFont(defaultFont.deriveFont(Font.BOLD)).build() + ).buildComponent()).xy(3, 7); + + builder.add("Center aligned").xy(1, 9); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label").build(), + LabelPresentationModel.builder().setHorizontalAlignment(HorizontalAlignment.CENTER).build() + ).buildComponent()).xy(3, 9); + + builder.add("End aligned").xy(1, 11); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label").build(), + LabelPresentationModel.builder().setHorizontalAlignment(HorizontalAlignment.TRAILING).build() + ).buildComponent()).xy(3, 11); + + builder.add("Center aligned with icon").xy(1, 13); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label") + .setIconFactory(info_black_24dp.factory()).build(), + LabelPresentationModel.builder().setHorizontalAlignment(HorizontalAlignment.CENTER).build() + ).buildComponent()).xy(3, 13); + + builder.add("End aligned with icon").xy(1, 15); + builder.add(new LabelProjection( + LabelContentModel.builder().setText("Sample label") + .setIconFactory(info_black_24dp.factory()).build(), + LabelPresentationModel.builder().setHorizontalAlignment(HorizontalAlignment.TRAILING).build() + ).buildComponent()).xy(3, 15); + + this.add(builder.build(), BorderLayout.CENTER); + } + + /** + * Main method for testing. + * + * @param args Ignored. + */ + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + JFrame.setDefaultLookAndFeelDecorated(true); + RadianceThemingCortex.GlobalScope.setSkin(new BusinessSkin()); + TestExoLabels frame = new TestExoLabels(); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + }); + } +} diff --git a/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/CustomComplexPopupMenuPresentationModel.java b/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/CustomComplexPopupMenuPresentationModel.java index 69b3093cd..d502b2bee 100644 --- a/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/CustomComplexPopupMenuPresentationModel.java +++ b/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/CustomComplexPopupMenuPresentationModel.java @@ -70,7 +70,6 @@ public class CustomComplexPopupMenuPresentationModel extends BaseCommandPopupMen public final LabelPresentationModel zoomLabelPresentationModel = LabelPresentationModel.builder() .setContentPadding(new Insets(6, 4, 6, 4)) - .setTextMaxLines(1) .setSingleLineDisplayPrototype("999%") .setHorizontalAlignment(HorizontalAlignment.CENTER) .build(); @@ -94,7 +93,6 @@ public class CustomComplexPopupMenuPresentationModel extends BaseCommandPopupMen public final LabelPresentationModel headerTitlePresentationModel = LabelPresentationModel.builder() .setContentPadding(new Insets(10, 16, 10, 16)) - .setTextMaxLines(1) .setFont(RadianceThemingCortex.GlobalScope.getFontPolicy(). getFontSet().getControlFont().deriveFont(Font.BOLD)) .build(); diff --git a/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/JCustomComplexPopupMenuPanel.java b/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/JCustomComplexPopupMenuPanel.java index 6f67ab72c..b93cd3460 100644 --- a/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/JCustomComplexPopupMenuPanel.java +++ b/demos/component-demo/src/main/java/org/pushingpixels/radiance/demo/component/common/custom/JCustomComplexPopupMenuPanel.java @@ -135,7 +135,6 @@ public Dimension preferredLayoutSize(Container parent) { LabelContentModel.builder().setText(edit.getTitle()).build(), LabelPresentationModel.builder() .setContentPadding(presentationModel.itemContentPadding) - .setTextMaxLines(1) .build(), defaultFont ); @@ -168,7 +167,6 @@ public Dimension preferredLayoutSize(Container parent) { LabelContentModel.builder().setText(zoom.getTitle()).build(), LabelPresentationModel.builder() .setContentPadding(presentationModel.itemContentPadding) - .setTextMaxLines(1) .build(), defaultFont ); @@ -355,7 +353,6 @@ public EditSection(CustomComplexPopupMenuContentModel.CustomComplexPopupMenuEdit JExoLabel titleLabel = new LabelProjection( LabelContentModel.builder().setText(edit.getTitle()).build(), LabelPresentationModel.builder() - .setTextMaxLines(1) .setContentPadding(presentationModel.itemContentPadding) .build() ).buildComponent(); @@ -405,7 +402,6 @@ public ZoomSection(CustomComplexPopupMenuContentModel.CustomComplexPopupMenuZoom JExoLabel titleLabel = new LabelProjection( LabelContentModel.builder().setText(zoom.getTitle()).build(), LabelPresentationModel.builder() - .setTextMaxLines(1) .setContentPadding(presentationModel.itemContentPadding) .build() ).buildComponent();