org.pushingpixels.flamingo.internal.ui.common.BasicRichTooltipPanelUI Maven / Gradle / Ivy
/*
* Copyright (c) 2005-2010 Flamingo 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 Flamingo Kirill Grouchnikov 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.flamingo.internal.ui.common;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.AffineTransform;
import java.text.AttributedString;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import org.pushingpixels.flamingo.api.common.RichTooltip;
import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities;
/**
* Basic UI for rich tooltip panel {@link JRichTooltipPanel}.
*
* @author Kirill Grouchnikov
*/
public class BasicRichTooltipPanelUI extends RichTooltipPanelUI {
/**
* The associated tooltip panel.
*/
protected JRichTooltipPanel richTooltipPanel;
protected java.util.List titleLabels;
protected java.util.List descriptionLabels;
protected JLabel mainImageLabel;
protected JSeparator footerSeparator;
protected JLabel footerImageLabel;
protected java.util.List footerLabels;
/*
* (non-Javadoc)
*
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
*/
public static ComponentUI createUI(JComponent c) {
return new BasicRichTooltipPanelUI();
}
public BasicRichTooltipPanelUI() {
this.titleLabels = new ArrayList();
this.descriptionLabels = new ArrayList();
this.footerLabels = new ArrayList();
}
/*
* (non-Javadoc)
*
* @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent)
*/
@Override
public void installUI(JComponent c) {
this.richTooltipPanel = (JRichTooltipPanel) c;
super.installUI(this.richTooltipPanel);
installDefaults();
installComponents();
installListeners();
this.richTooltipPanel.setLayout(createLayoutManager());
}
/*
* (non-Javadoc)
*
* @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent)
*/
@Override
public void uninstallUI(JComponent c) {
uninstallListeners();
uninstallComponents();
uninstallDefaults();
super.uninstallUI(this.richTooltipPanel);
}
/**
* Installs default settings for the associated rich tooltip panel.
*/
protected void installDefaults() {
Border b = this.richTooltipPanel.getBorder();
if (b == null || b instanceof UIResource) {
Border toSet = UIManager.getBorder("RichTooltipPanel.border");
if (toSet == null)
toSet = new BorderUIResource.CompoundBorderUIResource(
new LineBorder(FlamingoUtilities.getBorderColor()),
new EmptyBorder(2, 4, 3, 4));
this.richTooltipPanel.setBorder(toSet);
}
LookAndFeel.installProperty(this.richTooltipPanel, "opaque",
Boolean.TRUE);
}
/**
* Installs listeners on the associated rich tooltip panel.
*/
protected void installListeners() {
}
/**
* Installs components on the associated rich tooltip panel.
*/
protected void installComponents() {
}
/**
* Uninstalls default settings from the associated rich tooltip panel.
*/
protected void uninstallDefaults() {
LookAndFeel.uninstallBorder(this.richTooltipPanel);
}
/**
* Uninstalls listeners from the associated rich tooltip panel.
*/
protected void uninstallListeners() {
}
/**
* Uninstalls subcomponents from the associated rich tooltip panel.
*/
protected void uninstallComponents() {
this.removeExistingComponents();
}
@Override
public void update(Graphics g, JComponent c) {
this.paintBackground(g);
this.paint(g, c);
}
protected void paintBackground(Graphics g) {
Color main = FlamingoUtilities.getColor(Color.gray,
"Label.disabledForeground").brighter();
Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(new GradientPaint(0, 0, FlamingoUtilities.getLighterColor(
main, 0.9), 0, this.richTooltipPanel.getHeight(),
FlamingoUtilities.getLighterColor(main, 0.4)));
g2d.fillRect(0, 0, this.richTooltipPanel.getWidth(),
this.richTooltipPanel.getHeight());
g2d.setFont(FlamingoUtilities.getFont(this.richTooltipPanel,
"Ribbon.font", "Button.font", "Panel.font"));
g2d.dispose();
}
@Override
public void paint(Graphics g, JComponent c) {
}
protected LayoutManager createLayoutManager() {
return new RichTooltipPanelLayout();
}
protected class RichTooltipPanelLayout implements LayoutManager {
@Override
public void addLayoutComponent(String name, Component comp) {
}
@Override
public void removeLayoutComponent(Component comp) {
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return this.preferredLayoutSize(parent);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
Insets ins = parent.getInsets();
int gap = getLayoutGap();
Font font = FlamingoUtilities.getFont(parent, "Ribbon.font",
"Button.font", "Panel.font");
Font titleFont = font.deriveFont(Font.BOLD);
// the main text gets 200 pixels. The width is defined
// by this and the presence of the main text.
// The height is defined based on the width and the
// text broken into multiline paragraphs
int descTextWidth = getDescriptionTextWidth();
int width = ins.left + 2 * gap + descTextWidth + ins.right;
RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo();
FontRenderContext frc = new FontRenderContext(
new AffineTransform(), true, false);
if (tooltipInfo.getMainImage() != null) {
width += tooltipInfo.getMainImage().getWidth(null);
}
int fontHeight = parent.getFontMetrics(font).getHeight();
int height = ins.top;
// The title label
int titleTextHeight = 0;
AttributedString titleAttributedDescription = new AttributedString(
tooltipInfo.getTitle());
titleAttributedDescription.addAttribute(TextAttribute.FONT,
titleFont);
LineBreakMeasurer titleLineBreakMeasurer = new LineBreakMeasurer(
titleAttributedDescription.getIterator(), frc);
int maxTitleLineWidth = 0;
while (true) {
TextLayout tl = titleLineBreakMeasurer
.nextLayout(descTextWidth);
if (tl == null)
break;
titleTextHeight += fontHeight;
int lineWidth = (int) Math.ceil(tl.getBounds().getWidth());
maxTitleLineWidth = Math.max(maxTitleLineWidth, lineWidth);
}
height += titleTextHeight;
// The description text
int descriptionTextHeight = 0;
for (String descText : tooltipInfo.getDescriptionSections()) {
AttributedString descAttributedDescription = new AttributedString(
descText);
descAttributedDescription
.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer descLineBreakMeasurer = new LineBreakMeasurer(
descAttributedDescription.getIterator(), frc);
while (true) {
TextLayout tl = descLineBreakMeasurer
.nextLayout(descTextWidth);
if (tl == null)
break;
descriptionTextHeight += fontHeight;
}
// add an empty line after the paragraph
descriptionTextHeight += fontHeight;
}
if (!tooltipInfo.getDescriptionSections().isEmpty()) {
// remove the empty line after the last paragraph
descriptionTextHeight -= fontHeight;
// add gap between the title and the description
descriptionTextHeight += gap;
}
if (tooltipInfo.getMainImage() != null) {
height += Math.max(descriptionTextHeight, new JLabel(
new ImageIcon(tooltipInfo.getMainImage()))
.getPreferredSize().height);
} else {
height += descriptionTextHeight;
}
if ((tooltipInfo.getFooterImage() != null)
|| (tooltipInfo.getFooterSections().size() > 0)) {
height += gap;
// The footer separator
height += new JSeparator(JSeparator.HORIZONTAL)
.getPreferredSize().height;
height += gap;
int footerTextHeight = 0;
int availableWidth = descTextWidth;
if (tooltipInfo.getFooterImage() != null) {
availableWidth -= tooltipInfo.getFooterImage().getWidth(
null);
}
if (tooltipInfo.getMainImage() != null) {
availableWidth += tooltipInfo.getMainImage().getWidth(null);
}
for (String footerText : tooltipInfo.getFooterSections()) {
AttributedString footerAttributedDescription = new AttributedString(
footerText);
footerAttributedDescription.addAttribute(
TextAttribute.FONT, font);
LineBreakMeasurer footerLineBreakMeasurer = new LineBreakMeasurer(
footerAttributedDescription.getIterator(), frc);
while (true) {
TextLayout tl = footerLineBreakMeasurer
.nextLayout(availableWidth);
if (tl == null)
break;
footerTextHeight += fontHeight;
}
// add an empty line after the paragraph
footerTextHeight += fontHeight;
}
// remove the empty line after the last paragraph
footerTextHeight -= fontHeight;
if (tooltipInfo.getFooterImage() != null) {
height += Math.max(footerTextHeight, new JLabel(
new ImageIcon(tooltipInfo.getFooterImage()))
.getPreferredSize().height);
} else {
height += footerTextHeight;
}
}
height += ins.bottom;
// special case for rich tooltips that only have titles
if (tooltipInfo.getDescriptionSections().isEmpty()
&& (tooltipInfo.getMainImage() == null)
&& tooltipInfo.getFooterSections().isEmpty()
&& (tooltipInfo.getFooterImage() == null)) {
width = maxTitleLineWidth + 1 + ins.left + ins.right;
}
return new Dimension(width, height);
}
@Override
public void layoutContainer(Container parent) {
removeExistingComponents();
Font font = FlamingoUtilities.getFont(parent, "Ribbon.font",
"Button.font", "Panel.font");
Insets ins = richTooltipPanel.getInsets();
int y = ins.top;
RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo();
FontRenderContext frc = new FontRenderContext(
new AffineTransform(), true, false);
int gap = getLayoutGap();
int fontHeight = parent.getFontMetrics(font).getHeight();
Font titleFont = font.deriveFont(Font.BOLD);
boolean ltr = richTooltipPanel.getComponentOrientation()
.isLeftToRight();
// The title label
int titleLabelWidth = parent.getWidth() - ins.left - ins.right;
AttributedString titleAtributedDescription = new AttributedString(
tooltipInfo.getTitle());
titleAtributedDescription.addAttribute(TextAttribute.FONT,
titleFont);
LineBreakMeasurer titleLineBreakMeasurer = new LineBreakMeasurer(
titleAtributedDescription.getIterator(), frc);
int titleCurrOffset = 0;
while (true) {
TextLayout tl = titleLineBreakMeasurer
.nextLayout(titleLabelWidth);
if (tl == null)
break;
int charCount = tl.getCharacterCount();
String line = tooltipInfo.getTitle().substring(titleCurrOffset,
titleCurrOffset + charCount);
JLabel titleLabel = new JLabel(line);
titleLabel.setFont(titleFont);
titleLabels.add(titleLabel);
richTooltipPanel.add(titleLabel);
int currLabelWidth = titleLabel.getPreferredSize().width;
if (ltr) {
titleLabel.setBounds(ins.left, y, currLabelWidth,
fontHeight);
} else {
titleLabel.setBounds(parent.getWidth() - ins.right
- currLabelWidth, y, currLabelWidth, fontHeight);
}
y += titleLabel.getHeight();
titleCurrOffset += charCount;
}
y += gap;
// The main image
int x = ltr ? ins.left : parent.getWidth() - ins.right;
if (tooltipInfo.getMainImage() != null) {
mainImageLabel = new JLabel(new ImageIcon(tooltipInfo
.getMainImage()));
richTooltipPanel.add(mainImageLabel);
int mainImageWidth = mainImageLabel.getPreferredSize().width;
if (ltr) {
mainImageLabel.setBounds(x, y, mainImageWidth,
mainImageLabel.getPreferredSize().height);
x += mainImageWidth;
} else {
mainImageLabel.setBounds(x - mainImageWidth, y,
mainImageWidth,
mainImageLabel.getPreferredSize().height);
x -= mainImageWidth;
}
}
if (ltr) {
x += 2 * gap;
} else {
x -= 2 * gap;
}
// The description text
int descLabelWidth = ltr ? parent.getWidth() - x - ins.right : x
- ins.left;
for (String descText : tooltipInfo.getDescriptionSections()) {
AttributedString attributedDescription = new AttributedString(
descText);
attributedDescription.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
attributedDescription.getIterator(), frc);
int currOffset = 0;
while (true) {
TextLayout tl = lineBreakMeasurer
.nextLayout(descLabelWidth);
if (tl == null)
break;
int charCount = tl.getCharacterCount();
String line = descText.substring(currOffset, currOffset
+ charCount);
JLabel descLabel = new JLabel(line);
descriptionLabels.add(descLabel);
richTooltipPanel.add(descLabel);
int currDescWidth = descLabel.getPreferredSize().width;
if (ltr) {
descLabel.setBounds(x, y, currDescWidth, fontHeight);
} else {
descLabel.setBounds(x - currDescWidth, y,
currDescWidth, fontHeight);
}
y += descLabel.getHeight();
currOffset += charCount;
}
// add an empty line after the paragraph
y += fontHeight;
}
// remove the empty line after the last paragraph
y -= fontHeight;
if (mainImageLabel != null) {
y = Math.max(y, mainImageLabel.getY()
+ mainImageLabel.getHeight());
}
if ((tooltipInfo.getFooterImage() != null)
|| (tooltipInfo.getFooterSections().size() > 0)) {
y += gap;
// The footer separator
footerSeparator = new JSeparator(JSeparator.HORIZONTAL);
richTooltipPanel.add(footerSeparator);
footerSeparator.setBounds(ins.left, y, parent.getWidth()
- ins.left - ins.right, footerSeparator
.getPreferredSize().height);
y += footerSeparator.getHeight() + gap;
// The footer image
x = ltr ? ins.left : parent.getWidth() - ins.right;
if (tooltipInfo.getFooterImage() != null) {
footerImageLabel = new JLabel(new ImageIcon(tooltipInfo
.getFooterImage()));
richTooltipPanel.add(footerImageLabel);
int footerImageWidth = footerImageLabel.getPreferredSize().width;
if (ltr) {
footerImageLabel.setBounds(x, y, footerImageWidth,
footerImageLabel.getPreferredSize().height);
x += footerImageWidth + 2 * gap;
} else {
footerImageLabel.setBounds(x - footerImageWidth, y,
footerImageWidth, footerImageLabel
.getPreferredSize().height);
x -= (footerImageWidth + 2 * gap);
}
}
// The footer text
int footerLabelWidth = ltr ? parent.getWidth() - x - ins.right
: x - ins.left;
for (String footerText : tooltipInfo.getFooterSections()) {
AttributedString attributedDescription = new AttributedString(
footerText);
attributedDescription
.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
attributedDescription.getIterator(), frc);
int currOffset = 0;
while (true) {
TextLayout tl = lineBreakMeasurer
.nextLayout(footerLabelWidth);
if (tl == null)
break;
int charCount = tl.getCharacterCount();
String line = footerText.substring(currOffset,
currOffset + charCount);
JLabel footerLabel = new JLabel(line);
footerLabels.add(footerLabel);
richTooltipPanel.add(footerLabel);
int currLabelWidth = footerLabel.getPreferredSize().width;
if (ltr) {
footerLabel.setBounds(x, y, currLabelWidth,
fontHeight);
} else {
footerLabel.setBounds(x - currLabelWidth, y,
currLabelWidth, fontHeight);
}
y += footerLabel.getHeight();
currOffset += charCount;
}
// add an empty line after the paragraph
y += fontHeight;
}
// remove the empty line after the last paragraph
y -= fontHeight;
}
}
}
protected int getDescriptionTextWidth() {
return 200;
}
protected int getLayoutGap() {
return 4;
}
protected void removeExistingComponents() {
for (JLabel label : this.titleLabels)
this.richTooltipPanel.remove(label);
if (this.mainImageLabel != null) {
this.richTooltipPanel.remove(this.mainImageLabel);
}
for (JLabel label : this.descriptionLabels)
this.richTooltipPanel.remove(label);
if (this.footerSeparator != null) {
this.richTooltipPanel.remove(this.footerSeparator);
}
if (this.footerImageLabel != null) {
this.richTooltipPanel.remove(this.footerImageLabel);
}
for (JLabel label : this.footerLabels)
this.richTooltipPanel.remove(label);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy