org.apache.pivot.wtk.Theme Maven / Gradle / Ivy
/*
* 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;
import java.awt.Font;
import org.apache.pivot.collections.Dictionary;
import org.apache.pivot.collections.HashMap;
import org.apache.pivot.util.Service;
import org.apache.pivot.wtk.skin.BorderSkin;
import org.apache.pivot.wtk.skin.BoxPaneSkin;
import org.apache.pivot.wtk.skin.CardPaneSkin;
import org.apache.pivot.wtk.skin.ColorChooserButtonSkin;
import org.apache.pivot.wtk.skin.FillPaneSkin;
import org.apache.pivot.wtk.skin.FlowPaneSkin;
import org.apache.pivot.wtk.skin.GridPaneFillerSkin;
import org.apache.pivot.wtk.skin.GridPaneSkin;
import org.apache.pivot.wtk.skin.ImageViewSkin;
import org.apache.pivot.wtk.skin.LabelSkin;
import org.apache.pivot.wtk.skin.MovieViewSkin;
import org.apache.pivot.wtk.skin.PanelSkin;
import org.apache.pivot.wtk.skin.ScrollPaneSkin;
import org.apache.pivot.wtk.skin.SeparatorSkin;
import org.apache.pivot.wtk.skin.StackPaneSkin;
import org.apache.pivot.wtk.skin.TablePaneFillerSkin;
import org.apache.pivot.wtk.skin.TablePaneSkin;
import org.apache.pivot.wtk.skin.TextAreaSkin;
import org.apache.pivot.wtk.skin.TextPaneSkin;
import org.apache.pivot.wtk.skin.WindowSkin;
/**
* Base class for Pivot themes. A theme defines a complete "look and feel"
* for a Pivot application.
*
* Note that concrete Theme implementations should be declared as final. If
* multiple third-party libraries attempted to extend a theme, it would cause a
* conflict, as only one could be used in any given application.
*
* IMPORTANT All skin mappings must be added to the map, even non-static inner
* classes. Otherwise, the component's base class will attempt to install its
* own skin, which will result in the addition of duplicate listeners.
*/
public abstract class Theme {
protected HashMap, Class extends Skin>> componentSkinMap =
new HashMap, Class extends Skin>>();
private static Theme theme = null;
public static final String NAME_KEY = "name";
public static final String SIZE_KEY = "size";
public static final String BOLD_KEY = "bold";
public static final String ITALIC_KEY = "italic";
/**
* The service provider name (see {@link Service#getProvider(String)}).
*/
public static final String PROVIDER_NAME = "org.apache.pivot.wtk.Theme";
static {
theme = (Theme)Service.getProvider(PROVIDER_NAME);
if (theme == null) {
throw new ThemeNotFoundException();
}
}
public Theme() {
componentSkinMap.put(Border.class, BorderSkin.class);
componentSkinMap.put(BoxPane.class, BoxPaneSkin.class);
componentSkinMap.put(CardPane.class, CardPaneSkin.class);
componentSkinMap.put(ColorChooserButtonSkin.ColorChooserPopup.class,
ColorChooserButtonSkin.ColorChooserPopupSkin.class);
componentSkinMap.put(FillPane.class, FillPaneSkin.class);
componentSkinMap.put(FlowPane.class, FlowPaneSkin.class);
componentSkinMap.put(GridPane.class, GridPaneSkin.class);
componentSkinMap.put(GridPane.Filler.class, GridPaneFillerSkin.class);
componentSkinMap.put(ImageView.class, ImageViewSkin.class);
componentSkinMap.put(Label.class, LabelSkin.class);
componentSkinMap.put(MovieView.class, MovieViewSkin.class);
componentSkinMap.put(Panel.class, PanelSkin.class);
componentSkinMap.put(ScrollPane.class, ScrollPaneSkin.class);
componentSkinMap.put(Separator.class, SeparatorSkin.class);
componentSkinMap.put(StackPane.class, StackPaneSkin.class);
componentSkinMap.put(TablePane.class, TablePaneSkin.class);
componentSkinMap.put(TablePane.Filler.class, TablePaneFillerSkin.class);
componentSkinMap.put(TextArea.class, TextAreaSkin.class);
componentSkinMap.put(TextPane.class, TextPaneSkin.class);
componentSkinMap.put(Window.class, WindowSkin.class);
}
public final Class extends Skin> getSkinClass(Class extends Component> componentClass) {
return componentSkinMap.get(componentClass);
}
public abstract Font getFont();
public abstract void setFont(Font font);
/**
* Returns the skin class responsible for skinning the specified component
* class.
*
* @param componentClass
* The component class.
*
* @return
* The skin class, or null if no skin mapping exists for the
* component class.
*/
public Class extends Skin> get(Class extends Component> componentClass) {
if (componentClass == null) {
throw new IllegalArgumentException("Component class is null.");
}
return componentSkinMap.get(componentClass);
}
/**
* Sets the skin class responsible for skinning the specified component
* class.
*
* @param componentClass
* The component class.
*
* @param skinClass
* The skin class.
*/
public void set(Class extends Component> componentClass, Class extends Skin> skinClass) {
if (componentClass == null) {
throw new IllegalArgumentException("Component class is null.");
}
if (skinClass == null) {
throw new IllegalArgumentException("Skin class is null.");
}
componentSkinMap.put(componentClass, skinClass);
}
/**
* Gets the current theme, as determined by the {@linkplain #PROVIDER_NAME
* theme provider}.
*
* @throws IllegalStateException
* If a theme has not been installed.
*/
public static Theme getTheme() {
if (theme == null) {
throw new IllegalStateException("No installed theme.");
}
return theme;
}
/**
* Produce a font by describing it relative to the current theme's font
* @param dictionary A dictionary with any of the following keys:
*
* - {@value #NAME_KEY} - the family name of the font
* - {@value #SIZE_KEY} - the font size as an integer, or a string "x%" for a relative size
* - {@value #BOLD_KEY} - true/false
* - {@value #ITALIC_KEY} - true/false
*
* Omitted values are taken from the theme's font.
*/
public static Font deriveFont(Dictionary dictionary) {
Font font = theme.getFont();
String name = font.getName();
if (dictionary.containsKey(NAME_KEY)) {
name = (String)dictionary.get(NAME_KEY);
}
int size = font.getSize();
if (dictionary.containsKey(SIZE_KEY)) {
Object value = dictionary.get(SIZE_KEY);
if (value instanceof String) {
String string = (String)value;
if (string.endsWith("%")) {
float percentage = Float.parseFloat(string.substring(0, string.length() - 1)) / 100f;
size = Math.round(font.getSize() * percentage);
} else {
throw new IllegalArgumentException(value + " is not a valid font size.");
}
} else {
size = (Integer)value;
}
}
int style = font.getStyle();
if (dictionary.containsKey(BOLD_KEY)) {
boolean bold = (Boolean)dictionary.get(BOLD_KEY);
if (bold) {
style |= Font.BOLD;
} else {
style &= ~Font.BOLD;
}
}
if (dictionary.containsKey(ITALIC_KEY)) {
boolean italic = (Boolean)dictionary.get(ITALIC_KEY);
if (italic) {
style |= Font.ITALIC;
} else {
style &= ~Font.ITALIC;
}
}
return new Font(name, style, size);
}
}