1 /* 2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.javafx.experiments.dukepad.calculator; 27 28 import com.javafx.experiments.dukepad.core.Fonts; 29 import com.sun.javafx.util.Utils; 30 import com.sun.javafx.scene.control.skin.ButtonSkin; 31 import com.sun.javafx.scene.control.skin.TextFieldSkin; 32 import javafx.beans.value.ChangeListener; 33 import javafx.beans.value.ObservableValue; 34 import javafx.geometry.Insets; 35 import javafx.geometry.Pos; 36 import javafx.scene.Node; 37 import javafx.scene.control.Button; 38 import javafx.scene.control.ContentDisplay; 39 import javafx.scene.control.TextField; 40 import javafx.scene.layout.Background; 41 import javafx.scene.layout.BackgroundFill; 42 import javafx.scene.layout.CornerRadii; 43 import javafx.scene.layout.Region; 44 import javafx.scene.paint.*; 45 import javafx.scene.text.Font; 46 import javafx.scene.text.Text; 47 import javafx.scene.text.TextAlignment; 48 49 /** 50 * This is used in place of CSS because the CSS processing was just too slow. 51 */ 52 public class CalculatorTheme { 53 private static final Color SHADOW_C1 = new Color(1, 1, 1, .07); 54 private static final Color SHADOW_C2 = new Color(1, 1, 1, .7); 55 private static final Color SHADOW_C3 = new Color(1, 1, 1, .75); 56 57 private static final Color DARK_TEXT_COLOR = Color.BLACK; 58 private static final Color MID_TEXT_COLOR = Color.web("#333"); 59 private static final Color LIGHT_TEXT_COLOR = Color.WHITE; 60 private static final Color FOCUS_COLOR = Color.web("#039ED3"); 61 private static final Color FAINT_FOCUS_COLOR = Color.web("#039ED322"); 62 63 private static final Color BASE = Color.web("#ececec"); 64 private static final Color LIGHT_BASE = Color.web("#e4e4e4"); 65 private static final Color DARK_BASE = Color.web("#3d4148"); 66 private static final Color BLUE_BASE = Color.web("#237eb8"); 67 private static final Color ORANGE_BASE = Color.web("#e35f15"); 68 69 private static final Font FONT = Fonts.dosisSemiBold(50); 70 private static final Font DARK_FONT = Fonts.dosisSemiBold(65); 71 72 private static final Insets DARK_PADDING = new Insets(-7, 0, 7, 0); 73 private static final Insets BLUE_PADDING = new Insets(-4, 0, 4, 0); 74 75 private static final Insets SHADOW_INSETS = new Insets(0, 0, -1, 0); 76 private static final Insets OUTER_INSETS = Insets.EMPTY; 77 private static final Insets INNER_INSETS = new Insets(1); 78 private static final Insets BODY_INSETS = new Insets(2); 79 80 private static final CornerRadii SHADOW_RADII = new CornerRadii(3); 81 private static final CornerRadii OUTER_RADII = SHADOW_RADII; 82 private static final CornerRadii INNER_RADII = new CornerRadii(2); 83 private static final CornerRadii BODY_RADII = new CornerRadii(1); 84 85 public static final Background LIGHT_BUTTON_BACKGROUND = new Background( 86 new BackgroundFill(computeShadowHighlight(LIGHT_BASE), SHADOW_RADII, SHADOW_INSETS), 87 new BackgroundFill(computeOuterBorder(LIGHT_BASE), OUTER_RADII, OUTER_INSETS), 88 new BackgroundFill(computeInnerBorder(LIGHT_BASE), INNER_RADII, INNER_INSETS), 89 new BackgroundFill(computeBodyColor(LIGHT_BASE), BODY_RADII, BODY_INSETS) 90 ); 91 public static final Background DARK_BUTTON_BACKGROUND = new Background( 92 new BackgroundFill(computeShadowHighlight(DARK_BASE), SHADOW_RADII, SHADOW_INSETS), 93 new BackgroundFill(computeOuterBorder(DARK_BASE), OUTER_RADII, OUTER_INSETS), 94 new BackgroundFill(computeInnerBorder(DARK_BASE), INNER_RADII, INNER_INSETS), 95 new BackgroundFill(computeBodyColor(DARK_BASE), BODY_RADII, BODY_INSETS) 96 ); 97 public static final Background BLUE_BUTTON_BACKGROUND = new Background( 98 new BackgroundFill(computeShadowHighlight(BLUE_BASE), SHADOW_RADII, SHADOW_INSETS), 99 new BackgroundFill(computeOuterBorder(BLUE_BASE), OUTER_RADII, OUTER_INSETS), 100 new BackgroundFill(computeInnerBorder(BLUE_BASE), INNER_RADII, INNER_INSETS), 101 new BackgroundFill(computeBodyColor(BLUE_BASE), BODY_RADII, BODY_INSETS) 102 ); 103 public static final Background ORANGE_BUTTON_BACKGROUND = new Background( 104 new BackgroundFill(computeShadowHighlight(ORANGE_BASE), SHADOW_RADII, SHADOW_INSETS), 105 new BackgroundFill(computeOuterBorder(ORANGE_BASE), OUTER_RADII, OUTER_INSETS), 106 new BackgroundFill(computeInnerBorder(ORANGE_BASE), INNER_RADII, INNER_INSETS), 107 new BackgroundFill(computeBodyColor(ORANGE_BASE), BODY_RADII, BODY_INSETS) 108 ); 109 110 private static final Paint LIGHT_BUTTON_FILL = computeTextFillColor(LIGHT_BASE); 111 private static final Paint DARK_BUTTON_FILL = computeTextFillColor(DARK_BASE); 112 private static final Paint BLUE_BUTTON_FILL = computeTextFillColor(BLUE_BASE); 113 private static final Paint ORANGE_BUTTON_FILL = computeTextFillColor(ORANGE_BASE); 114 115 116 private static final Color LIGHT_BASE__PRESSED = Utils.deriveColor(LIGHT_BASE, -.06); 117 private static final Color DARK_BASE__PRESSED = Utils.deriveColor(DARK_BASE, -.06); 118 private static final Color BLUE_BASE__PRESSED = Utils.deriveColor(BLUE_BASE, -.06); 119 private static final Color ORANGE_BASE__PRESSED = Utils.deriveColor(ORANGE_BASE, -.06); 120 121 public static final Background LIGHT_BUTTON_BACKGROUND__PRESSED = new Background( 122 new BackgroundFill(computeShadowHighlight(LIGHT_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 123 new BackgroundFill(computeOuterBorder(LIGHT_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 124 new BackgroundFill(computeInnerBorder(LIGHT_BASE__PRESSED), INNER_RADII, INNER_INSETS), 125 new BackgroundFill(computeBodyColor(LIGHT_BASE__PRESSED), BODY_RADII, BODY_INSETS) 126 ); 127 public static final Background DARK_BUTTON_BACKGROUND__PRESSED = new Background( 128 new BackgroundFill(computeShadowHighlight(DARK_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 129 new BackgroundFill(computeOuterBorder(DARK_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 130 new BackgroundFill(computeInnerBorder(DARK_BASE__PRESSED), INNER_RADII, INNER_INSETS), 131 new BackgroundFill(computeBodyColor(DARK_BASE__PRESSED), BODY_RADII, BODY_INSETS) 132 ); 133 public static final Background BLUE_BUTTON_BACKGROUND__PRESSED = new Background( 134 new BackgroundFill(computeShadowHighlight(BLUE_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 135 new BackgroundFill(computeOuterBorder(BLUE_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 136 new BackgroundFill(computeInnerBorder(BLUE_BASE__PRESSED), INNER_RADII, INNER_INSETS), 137 new BackgroundFill(computeBodyColor(BLUE_BASE__PRESSED), BODY_RADII, BODY_INSETS) 138 ); 139 public static final Background ORANGE_BUTTON_BACKGROUND__PRESSED = new Background( 140 new BackgroundFill(computeShadowHighlight(ORANGE_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 141 new BackgroundFill(computeOuterBorder(ORANGE_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 142 new BackgroundFill(computeInnerBorder(ORANGE_BASE__PRESSED), INNER_RADII, INNER_INSETS), 143 new BackgroundFill(computeBodyColor(ORANGE_BASE__PRESSED), BODY_RADII, BODY_INSETS) 144 ); 145 146 public static void styleLightButton(Button button) { 147 styleButton(button, LIGHT_BUTTON_BACKGROUND, LIGHT_BUTTON_BACKGROUND__PRESSED, LIGHT_BUTTON_FILL); 148 } 149 150 public static void styleDarkButton(Button button) { 151 styleButton(button, DARK_BUTTON_BACKGROUND, DARK_BUTTON_BACKGROUND__PRESSED, DARK_BUTTON_FILL); 152 button.setFont(DARK_FONT); 153 button.setPadding(DARK_PADDING); 154 } 155 156 public static void styleBlueButton(Button button) { 157 styleButton(button, BLUE_BUTTON_BACKGROUND, BLUE_BUTTON_BACKGROUND__PRESSED, BLUE_BUTTON_FILL); 158 button.setPadding(BLUE_PADDING); 159 } 160 161 public static void styleOrangeButton(Button button) { 162 styleButton(button, ORANGE_BUTTON_BACKGROUND, ORANGE_BUTTON_BACKGROUND__PRESSED, ORANGE_BUTTON_FILL); 163 button.setTextFill(Color.WHITE); 164 } 165 166 public static void styleEqualsButton(Button button) { 167 styleOrangeButton(button); 168 button.setFont(DARK_FONT); 169 button.setPadding(DARK_PADDING); 170 } 171 172 public static void styleBackground(Region parent) { 173 parent.setBackground(new Background(new BackgroundFill(BASE, null, null))); 174 } 175 176 public static void styleTextField(TextField textField) { 177 final Color controlInnerBackground = Utils.deriveColor(BASE, .8); 178 final Color textInnerColor = Utils.ladder(controlInnerBackground, new Stop[]{ 179 new Stop(.45, LIGHT_TEXT_COLOR), 180 new Stop(.46, LIGHT_TEXT_COLOR), 181 new Stop(.59, DARK_TEXT_COLOR), 182 new Stop(.60, MID_TEXT_COLOR) 183 }); 184 final Color textBoxBorder = Utils.ladder(BASE, new Stop[] { 185 new Stop(.1, Color.BLACK), 186 new Stop(.3, Utils.deriveColor(BASE, -.15)) 187 }); 188 final Paint backgroundColor = new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 189 new Stop(0, Utils.deriveColor(textBoxBorder, -.1)), 190 new Stop(1, textBoxBorder) 191 ); 192 final Paint backgroundColor2 = new LinearGradient(0, 0, 0, 5, false, CycleMethod.NO_CYCLE, 193 new Stop(0, Utils.deriveColor(controlInnerBackground, -.09)), 194 new Stop(1, controlInnerBackground) 195 ); 196 textField.setFont(FONT); 197 textField.setBackground(new Background( 198 new BackgroundFill(backgroundColor, new CornerRadii(3), Insets.EMPTY), 199 new BackgroundFill(backgroundColor2, new CornerRadii(2), new Insets(1)) 200 )); 201 textField.setPadding(new Insets(.333333 * FONT.getSize(), .583 * FONT.getSize(), .333333 * FONT.getSize(), .583 * FONT.getSize())); 202 TextFieldSkin skin = new TextFieldSkin(textField) { 203 { 204 textFill.set(textInnerColor); 205 } 206 }; 207 textField.setSkin(skin); 208 } 209 210 private static void styleButton(final Button button, final Background background, final Background pressed, Paint textFill) { 211 button.setBackground(background); 212 button.setTextFill(textFill); 213 button.setAlignment(Pos.CENTER); 214 button.setTextAlignment(TextAlignment.CENTER); 215 button.setContentDisplay(ContentDisplay.LEFT); 216 button.setPadding(Insets.EMPTY); 217 button.setFont(FONT); 218 button.pressedProperty().addListener(new ChangeListener<Boolean>() { 219 @Override 220 public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 221 if (newValue) { 222 button.setBackground(pressed); 223 } else { 224 button.setBackground(background); 225 } 226 } 227 }); 228 ButtonSkin skin = new ButtonSkin(button); 229 button.setSkin(skin); 230 // Workaround a bug 231 ((Text)skin.getChildren().get(0)).fillProperty().bind(button.textFillProperty()); 232 } 233 234 /* 235 .button:hover { 236 -fx-color: -fx-hover-base; 237 } 238 -fx-hover-base: ladder( 239 -fx-base, 240 derive(-fx-base,20%) 20%, 241 derive(-fx-base,30%) 35%, 242 derive(-fx-base,40%) 50% 243 ); 244 */ 245 246 /* 247 .button:focused { 248 -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color, -fx-faint-focus-color, -fx-body-color; 249 -fx-background-insets: -0.2, 1, 2, -1.4, 2.6; 250 -fx-background-radius: 3, 2, 1, 4, 1; 251 } 252 */ 253 254 /* 255 .background { 256 -fx-background-color: -fx-background; 257 } 258 */ 259 260 private static Color deriveBackground(Color base) { 261 return Utils.deriveColor(base, .264); 262 } 263 264 private static Paint computeShadowHighlight(Color base) { 265 /* 266 -fx-shadow-highlight-color: ladder( 267 -fx-background, 268 rgba(255,255,255,0.07) 0%, 269 rgba(255,255,255,0.07) 20%, 270 rgba(255,255,255,0.07) 70%, 271 rgba(255,255,255,0.7) 90%, 272 rgba(255,255,255,0.75) 100% 273 ); 274 */ 275 return Utils.ladder(deriveBackground(base), new Stop[] { 276 new Stop(0, SHADOW_C1), 277 new Stop(.2, SHADOW_C1), 278 new Stop(.7, SHADOW_C1), 279 new Stop(.9, SHADOW_C2), 280 new Stop(1, SHADOW_C3) 281 }); 282 } 283 284 private static Paint computeOuterBorder(Color color) { 285 /* derive(-fx-color,-23%); */ 286 return Utils.deriveColor(color, -.23); 287 } 288 289 private static Paint computeInnerBorder(Color color) { 290 /* 291 -fx-inner-border: linear-gradient(to bottom, 292 ladder( 293 -fx-color, 294 derive(-fx-color,30%) 0%, 295 derive(-fx-color,20%) 40%, 296 derive(-fx-color,25%) 60%, 297 derive(-fx-color,55%) 80%, 298 derive(-fx-color,55%) 90%, 299 derive(-fx-color,75%) 100% 300 ), 301 ladder( 302 -fx-color, 303 derive(-fx-color,20%) 0%, 304 derive(-fx-color,10%) 20%, 305 derive(-fx-color,5%) 40%, 306 derive(-fx-color,-2%) 60%, 307 derive(-fx-color,-5%) 100% 308 )); 309 */ 310 return new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 311 new Stop(0, Utils.ladder(color, new Stop[] { 312 new Stop(0, Utils.deriveColor(color, .3)), 313 new Stop(.4, Utils.deriveColor(color, .2)), 314 new Stop(.6, Utils.deriveColor(color, .25)), 315 new Stop(.8, Utils.deriveColor(color, .55)), 316 new Stop(.9, Utils.deriveColor(color, .55)), 317 new Stop(1, Utils.deriveColor(color, .75)) 318 })), 319 new Stop(1, Utils.ladder(color, new Stop[] { 320 new Stop(0, Utils.deriveColor(color, .2)), 321 new Stop(.2, Utils.deriveColor(color, .1)), 322 new Stop(.4, Utils.deriveColor(color, .05)), 323 new Stop(.6, Utils.deriveColor(color, -.02)), 324 new Stop(1, Utils.deriveColor(color, -.05)) 325 })) 326 ); 327 } 328 329 private static Paint computeBodyColor(Color color) { 330 /* 331 -fx-body-color: linear-gradient(to bottom, 332 ladder( 333 -fx-color, 334 derive(-fx-color,8%) 75%, 335 derive(-fx-color,10%) 80% 336 ), 337 derive(-fx-color,-8%)); 338 */ 339 return new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 340 new Stop(0, Utils.ladder(color, new Stop[] { 341 new Stop(.75, Utils.deriveColor(color, .08)), 342 new Stop(.80, Utils.deriveColor(color, .10)) 343 })), 344 new Stop(1, Utils.deriveColor(color, -.08)) 345 ); 346 347 } 348 349 private static Color computeTextFillColor(Color color) { 350 /* 351 -fx-text-base-color: ladder( 352 -fx-color, 353 -fx-light-text-color 45%, 354 -fx-dark-text-color 46%, 355 -fx-dark-text-color 59%, 356 -fx-mid-text-color 60% 357 ); 358 */ 359 return Utils.ladder(color, new Stop[] { 360 new Stop(.45, LIGHT_TEXT_COLOR), 361 new Stop(.46, LIGHT_TEXT_COLOR), 362 new Stop(.59, DARK_TEXT_COLOR), 363 new Stop(.60, MID_TEXT_COLOR) 364 }); 365 } 366 }