/* * Copyright 2014 MovingBlocks * * 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 org.terasology.rendering.nui.skin; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.collect.Table; import org.terasology.math.Border; import org.terasology.rendering.assets.texture.TextureRegion; import org.terasology.rendering.assets.font.Font; import org.terasology.rendering.nui.Color; import org.terasology.rendering.nui.HorizontalAlign; import org.terasology.rendering.nui.ScaleMode; import org.terasology.rendering.nui.UIWidget; import org.terasology.rendering.nui.VerticalAlign; import org.terasology.utilities.ReflectionUtil; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** */ public class UISkinBuilder { private UISkin baseSkin; private Set<String> families = Sets.newLinkedHashSet(); private Set<StyleKey> baseStyleKeys = Sets.newLinkedHashSet(); private Map<String, UIStyleFragment> baseStyles = Maps.newHashMap(); private Table<String, StyleKey, UIStyleFragment> elementStyles = HashBasedTable.create(); private UIStyleFragment currentStyle = new UIStyleFragment(); private String currentFamily = ""; private Class<? extends UIWidget> currentElement; private String currentPart = ""; private String currentMode = ""; private void saveStyle() { if (currentFamily.isEmpty() && currentElement != null) { baseStyleKeys.add(new StyleKey(currentElement, currentPart, currentMode)); } if (currentElement != null) { elementStyles.put(currentFamily, new StyleKey(currentElement, currentPart, currentMode), currentStyle); } else { baseStyles.put(currentFamily, currentStyle); } currentStyle = new UIStyleFragment(); } public UISkinBuilder setBaseSkin(UISkin skin) { this.baseSkin = skin; return this; } public UISkinBuilder setFamily(String family) { saveStyle(); families.add(family); currentFamily = family; currentElement = null; currentPart = ""; currentMode = ""; return this; } public UISkinBuilder setElementClass(Class<? extends UIWidget> widget) { saveStyle(); currentElement = widget; currentMode = ""; currentPart = ""; return this; } public UISkinBuilder setElementPart(String part) { if (currentElement == null) { throw new IllegalStateException("Element class must be set before element part"); } saveStyle(); currentPart = part; currentMode = ""; return this; } public UISkinBuilder setElementMode(String mode) { if (currentElement == null) { throw new IllegalStateException("Element class must be set before element mode"); } saveStyle(); currentMode = mode; return this; } public UISkinBuilder setBackground(TextureRegion background) { currentStyle.setBackground(background); return this; } public UISkinBuilder setBackgroundBorder(Border border) { currentStyle.setBackgroundBorder(border); return this; } public UISkinBuilder setBackgroundMode(ScaleMode mode) { currentStyle.setBackgroundScaleMode(mode); return this; } public UISkinBuilder setFixedWidth(int width) { currentStyle.setFixedWidth(width); return this; } public UISkinBuilder setFixedHeight(int height) { currentStyle.setFixedHeight(height); return this; } public UISkinBuilder setHorizontalAlignment(HorizontalAlign align) { currentStyle.setAlignmentH(align); return this; } public UISkinBuilder setVerticalAlignment(VerticalAlign align) { currentStyle.setAlignmentV(align); return this; } public UISkinBuilder setMargin(Border margin) { currentStyle.setMargin(margin); return this; } public UISkinBuilder setTextureScaleMode(ScaleMode scaleMode) { currentStyle.setTextureScaleMode(scaleMode); return this; } public UISkinBuilder setFont(Font font) { currentStyle.setFont(font); return this; } public UISkinBuilder setTextColor(Color color) { currentStyle.setTextColor(color); return this; } public UISkinBuilder setTextShadowColor(Color color) { currentStyle.setTextShadowColor(color); return this; } public UISkinBuilder setTextShadowed(boolean shadowed) { currentStyle.setTextShadowed(shadowed); return this; } public UISkinBuilder setTextHorizontalAlignment(HorizontalAlign hAlign) { currentStyle.setTextAlignmentH(hAlign); return this; } public UISkinBuilder setTextVerticalAlignment(VerticalAlign vAlign) { currentStyle.setTextAlignmentV(vAlign); return this; } public UISkinBuilder setTextUnderlined(boolean underlined) { currentStyle.setTextUnderlined(underlined); return this; } public UISkinBuilder setStyleFragment(UIStyleFragment fragment) { currentStyle = fragment; return this; } public UISkinData build() { saveStyle(); Map<String, UIStyleFamily> skinFamilies = Maps.newHashMap(); if (baseSkin != null) { UIStyle rootStyle = new UIStyle(baseSkin.getDefaultStyle()); baseStyles.get("").applyTo(rootStyle); skinFamilies.put("", buildFamily("", baseSkin)); for (String family : families) { skinFamilies.put(family, buildFamily(family, baseSkin)); } for (String family : baseSkin.getFamilies()) { if (!skinFamilies.containsKey(family)) { skinFamilies.put(family, baseSkin.getFamily(family)); } } return new UISkinData(skinFamilies); } else { UIStyle rootStyle = new UIStyle(); baseStyles.get("").applyTo(rootStyle); skinFamilies.put("", buildFamily("", rootStyle)); for (String family : families) { skinFamilies.put(family, buildFamily(family, rootStyle)); } return new UISkinData(skinFamilies); } } private UIStyleFamily buildFamily(String family, UISkin skin) { UIStyleFamily baseFamily = skin.getFamily(family); UIStyle baseStyle = new UIStyle(skin.getDefaultStyleFor(family)); if (!family.isEmpty()) { UIStyleFragment fragment = baseStyles.get(family); fragment.applyTo(baseStyle); } Set<StyleKey> inheritedStyleKey = Sets.newLinkedHashSet(); for (Class<? extends UIWidget> widget : baseFamily.getWidgets()) { inheritedStyleKey.add(new StyleKey(widget, "", "")); for (String part : baseFamily.getPartsFor(widget)) { inheritedStyleKey.add(new StyleKey(widget, part, "")); for (String mode : baseFamily.getModesFor(widget, part)) { inheritedStyleKey.add(new StyleKey(widget, part, mode)); } } } Map<Class<? extends UIWidget>, Table<String, String, UIStyle>> familyStyles = Maps.newHashMap(); Map<StyleKey, UIStyleFragment> styleLookup = elementStyles.row(family); Map<StyleKey, UIStyleFragment> baseStyleLookup = (family.isEmpty()) ? Maps.<StyleKey, UIStyleFragment>newHashMap() : elementStyles.row(""); for (StyleKey styleKey : Sets.union(Sets.union(styleLookup.keySet(), baseStyleKeys), inheritedStyleKey)) { UIStyle elementStyle = new UIStyle(baseSkin.getStyleFor(family, styleKey.element, styleKey.part, styleKey.mode)); baseStyles.get("").applyTo(elementStyle); baseStyles.get(family).applyTo(elementStyle); List<Class<? extends UIWidget>> inheritanceTree = ReflectionUtil.getInheritanceTree(styleKey.element, UIWidget.class); applyStylesForInheritanceTree(inheritanceTree, "", "", elementStyle, styleLookup, baseStyleLookup); if (!styleKey.part.isEmpty()) { applyStylesForInheritanceTree(inheritanceTree, styleKey.part, "", elementStyle, styleLookup, baseStyleLookup); } if (!styleKey.mode.isEmpty()) { applyStylesForInheritanceTree(inheritanceTree, styleKey.part, styleKey.mode, elementStyle, styleLookup, baseStyleLookup); } Table<String, String, UIStyle> elementTable = familyStyles.get(styleKey.element); if (elementTable == null) { elementTable = HashBasedTable.create(); familyStyles.put(styleKey.element, elementTable); } elementTable.put(styleKey.part, styleKey.mode, elementStyle); } return new UIStyleFamily(baseStyle, familyStyles); } private UIStyleFamily buildFamily(String family, UIStyle defaultStyle) { UIStyle baseStyle = new UIStyle(defaultStyle); if (!family.isEmpty()) { UIStyleFragment fragment = baseStyles.get(family); fragment.applyTo(baseStyle); } Map<Class<? extends UIWidget>, Table<String, String, UIStyle>> familyStyles = Maps.newHashMap(); Map<StyleKey, UIStyleFragment> styleLookup = elementStyles.row(family); Map<StyleKey, UIStyleFragment> baseStyleLookup = (family.isEmpty()) ? Maps.<StyleKey, UIStyleFragment>newHashMap() : elementStyles.row(""); for (StyleKey styleKey : Sets.union(styleLookup.keySet(), baseStyleKeys)) { UIStyle elementStyle = new UIStyle(baseStyle); List<Class<? extends UIWidget>> inheritanceTree = ReflectionUtil.getInheritanceTree(styleKey.element, UIWidget.class); applyStylesForInheritanceTree(inheritanceTree, "", "", elementStyle, styleLookup, baseStyleLookup); if (!styleKey.part.isEmpty()) { applyStylesForInheritanceTree(inheritanceTree, styleKey.part, "", elementStyle, styleLookup, baseStyleLookup); } if (!styleKey.mode.isEmpty()) { applyStylesForInheritanceTree(inheritanceTree, styleKey.part, styleKey.mode, elementStyle, styleLookup, baseStyleLookup); } Table<String, String, UIStyle> elementTable = familyStyles.get(styleKey.element); if (elementTable == null) { elementTable = HashBasedTable.create(); familyStyles.put(styleKey.element, elementTable); } elementTable.put(styleKey.part, styleKey.mode, elementStyle); } return new UIStyleFamily(baseStyle, familyStyles); } private void applyStylesForInheritanceTree(List<Class<? extends UIWidget>> inheritanceTree, String part, String mode, UIStyle elementStyle, Map<StyleKey, UIStyleFragment> styleLookup, Map<StyleKey, UIStyleFragment> baseStyleLookup) { for (Class<? extends UIWidget> element : inheritanceTree) { StyleKey key = new StyleKey(element, part, mode); UIStyleFragment baseElementStyle = baseStyleLookup.get(key); if (baseElementStyle != null) { baseElementStyle.applyTo(elementStyle); } UIStyleFragment elemStyle = styleLookup.get(key); if (elemStyle != null) { elemStyle.applyTo(elementStyle); } } } private static final class StyleKey { private Class<? extends UIWidget> element; private String part; private String mode; private StyleKey(Class<? extends UIWidget> element, String part, String mode) { this.element = element; this.part = part; this.mode = mode; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof StyleKey) { StyleKey other = (StyleKey) obj; return Objects.equals(other.element, element) && Objects.equals(other.part, part) && Objects.equals(other.mode, mode); } return false; } @Override public int hashCode() { return Objects.hash(element, part, mode); } @Override public String toString() { return element.getSimpleName() + ":" + part + ":" + mode; } } }