blob: 2a47f9d495d4cde9987efb2eb5495e6ab47019ce [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 org.apache.pivot.wtk.skin;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import org.apache.pivot.collections.Dictionary;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.Dimensions;
import org.apache.pivot.wtk.GraphicsUtilities;
import org.apache.pivot.wtk.Insets;
import org.apache.pivot.wtk.Platform;
import org.apache.pivot.wtk.Separator;
import org.apache.pivot.wtk.SeparatorListener;
import org.apache.pivot.wtk.Theme;
/**
* Separator skin.
*/
public class SeparatorSkin extends ComponentSkin implements SeparatorListener {
private Font font;
private Color color;
private Color headingColor;
private int thickness;
private Insets padding;
public SeparatorSkin() {
Theme theme = Theme.getTheme();
font = theme.getFont().deriveFont(Font.BOLD);
color = defaultForegroundColor();
headingColor = defaultForegroundColor();
thickness = 1;
padding = new Insets(4, 0, 4, 4);
}
@Override
public void install(Component component) {
super.install(component);
Separator separator = (Separator) component;
separator.getSeparatorListeners().add(this);
}
@Override
public int getPreferredWidth(int height) {
int preferredWidth = 0;
Separator separator = (Separator) getComponent();
String heading = separator.getHeading();
if (heading != null && heading.length() > 0) {
FontRenderContext fontRenderContext = Platform.getFontRenderContext();
Rectangle2D headingBounds = font.getStringBounds(heading, fontRenderContext);
preferredWidth = (int) Math.ceil(headingBounds.getWidth())
+ (padding.left + padding.right);
}
return preferredWidth;
}
@Override
public int getPreferredHeight(int width) {
int preferredHeight = thickness;
Separator separator = (Separator) getComponent();
String heading = separator.getHeading();
if (heading != null && heading.length() > 0) {
FontRenderContext fontRenderContext = Platform.getFontRenderContext();
LineMetrics lm = font.getLineMetrics(heading, fontRenderContext);
preferredHeight = Math.max(
(int) Math.ceil(lm.getAscent() + lm.getDescent() + lm.getLeading()),
preferredHeight);
}
preferredHeight += (padding.top + padding.bottom);
return preferredHeight;
}
@Override
public Dimensions getPreferredSize() {
int preferredWidth = 0;
int preferredHeight = thickness;
Separator separator = (Separator) getComponent();
String heading = separator.getHeading();
if (heading != null && heading.length() > 0) {
FontRenderContext fontRenderContext = Platform.getFontRenderContext();
Rectangle2D headingBounds = font.getStringBounds(heading, fontRenderContext);
LineMetrics lm = font.getLineMetrics(heading, fontRenderContext);
preferredWidth = (int) Math.ceil(headingBounds.getWidth());
preferredHeight = Math.max(
(int) Math.ceil(lm.getAscent() + lm.getDescent() + lm.getLeading()),
preferredHeight);
}
preferredHeight += (padding.top + padding.bottom);
preferredWidth += (padding.left + padding.right);
return new Dimensions(preferredWidth, preferredHeight);
}
@Override
public void layout() {
// No-op
}
@Override
public void paint(Graphics2D graphics) {
Separator separator = (Separator) getComponent();
int width = getWidth();
int separatorY = padding.top;
String heading = separator.getHeading();
if (heading != null && heading.length() > 0) {
FontRenderContext fontRenderContext = Platform.getFontRenderContext();
LineMetrics lm = font.getLineMetrics(heading, fontRenderContext);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
fontRenderContext.getAntiAliasingHint());
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
fontRenderContext.getFractionalMetricsHint());
graphics.setFont(font);
graphics.setPaint(headingColor);
graphics.drawString(heading, padding.left, lm.getAscent() + padding.top);
Rectangle2D headingBounds = font.getStringBounds(heading, fontRenderContext);
Area titleClip = new Area(graphics.getClip());
titleClip.subtract(new Area(new Rectangle2D.Double(padding.left, padding.top,
headingBounds.getWidth() + padding.right, headingBounds.getHeight())));
graphics.clip(titleClip);
separatorY += (lm.getAscent() + lm.getDescent()) / 2 + 1;
}
graphics.setStroke(new BasicStroke(thickness));
graphics.setColor(color);
graphics.drawLine(0, separatorY, width, separatorY);
}
/**
* @return <tt>false</tt>; spacers are not focusable.
*/
@Override
public boolean isFocusable() {
return false;
}
/**
* @return The font used in rendering the Separator's heading.
*/
public Font getFont() {
return font;
}
/**
* Sets the font used in rendering the Separator's heading.
*
* @param font The new font for the heading.
*/
public void setFont(Font font) {
if (font == null) {
throw new IllegalArgumentException("font is null.");
}
this.font = font;
invalidateComponent();
}
/**
* Sets the font used in rendering the Separator's heading.
*
* @param font A {@linkplain ComponentSkin#decodeFont(String) font specification}.
*/
public final void setFont(String font) {
if (font == null) {
throw new IllegalArgumentException("font is null.");
}
setFont(decodeFont(font));
}
/**
* Sets the font used in rendering the Separator's heading.
*
* @param font A dictionary {@link Theme#deriveFont describing a font}.
*/
public final void setFont(Dictionary<String, ?> font) {
if (font == null) {
throw new IllegalArgumentException("font is null.");
}
setFont(Theme.deriveFont(font));
}
/**
* @return The color of the Separator's horizontal rule.
*/
public Color getColor() {
return color;
}
/**
* Sets the color of the Separator's horizontal rule.
*
* @param color The new color for the horizontal rule.
*/
public void setColor(Color color) {
if (color == null) {
throw new IllegalArgumentException("color is null.");
}
this.color = color;
repaintComponent();
}
/**
* Sets the color of the Separator's horizontal rule.
*
* @param color Any of the {@linkplain GraphicsUtilities#decodeColor color
* values recognized by Pivot}.
*/
public final void setColor(String color) {
if (color == null) {
throw new IllegalArgumentException("color is null.");
}
setColor(GraphicsUtilities.decodeColor(color));
}
/**
* @return The color of the text in the heading.
*/
public Color getHeadingColor() {
return headingColor;
}
/**
* Sets the color of the text in the heading.
*
* @param headingColor The new color for the heading text.
*/
public void setHeadingColor(Color headingColor) {
if (headingColor == null) {
throw new IllegalArgumentException("headingColor is null.");
}
this.headingColor = headingColor;
repaintComponent();
}
/**
* Sets the color of the text in the heading.
*
* @param headingColor Any of the {@linkplain GraphicsUtilities#decodeColor
* color values recognized by Pivot}.
*/
public final void setHeadingColor(String headingColor) {
if (headingColor == null) {
throw new IllegalArgumentException("headingColor is null.");
}
setHeadingColor(GraphicsUtilities.decodeColor(headingColor));
}
/**
* @return The thickness of the Separator's horizontal rule.
*/
public int getThickness() {
return thickness;
}
/**
* Sets the thickness of the Separator's horizontal rule.
*
* @param thickness The new rule thickness (in pixels).
*/
public void setThickness(int thickness) {
if (thickness < 0) {
throw new IllegalArgumentException("thickness is negative.");
}
this.thickness = thickness;
invalidateComponent();
}
/**
* Sets the thickness of the Separator's horizontal rule.
*
* @param thickness The new integer value for the rule thickness (in pixels).
*/
public final void setThickness(Number thickness) {
if (thickness == null) {
throw new IllegalArgumentException("thickness is null.");
}
setThickness(thickness.intValue());
}
/**
* @return The amount of space surrounding (left/right) the Separator's
* heading, and above and below the entire component.
*/
public Insets getPadding() {
return padding;
}
/**
* Sets the amount of space to leave around the Separator's heading, and
* above and below the entire component.
*
* @param padding The new padding values.
*/
public void setPadding(Insets padding) {
if (padding == null) {
throw new IllegalArgumentException("padding is null.");
}
this.padding = padding;
invalidateComponent();
}
/**
* Sets the amount of space to leave around the Separator's heading, and
* above and below the entire component.
*
* @param padding A dictionary with keys in the set {left, top, bottom,
* right}.
*/
public final void setPadding(Dictionary<String, ?> padding) {
if (padding == null) {
throw new IllegalArgumentException("padding is null.");
}
setPadding(new Insets(padding));
}
/**
* Sets the amount of space to leave around the Separator's heading, and
* above and below the entire component.
*
* @param padding The new single padding value for all areas.
*/
public final void setPadding(int padding) {
setPadding(new Insets(padding));
}
/**
* Sets the amount of space to leave around the Separator's heading, and
* above and below the entire component.
*
* @param padding The new integer value to use for padding in all areas.
*/
public final void setPadding(Number padding) {
if (padding == null) {
throw new IllegalArgumentException("padding is null.");
}
setPadding(padding.intValue());
}
/**
* Sets the amount of space to leave around the Separator's heading, and
* above and below the entire component.
*
* @param padding A string containing an integer or a JSON dictionary with
* keys left, top, bottom, and/or right.
*/
public final void setPadding(String padding) {
if (padding == null) {
throw new IllegalArgumentException("padding is null.");
}
setPadding(Insets.decode(padding));
}
// Separator events
@Override
public void headingChanged(Separator separator, String previousHeading) {
invalidateComponent();
}
}