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 javafx.scene.control.skin.ButtonSkin; 31 import 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.control.Button; 37 import javafx.scene.control.ContentDisplay; 38 import javafx.scene.control.TextField; 39 import javafx.scene.layout.Background; 40 import javafx.scene.layout.BackgroundFill; 41 import javafx.scene.layout.CornerRadii; 42 import javafx.scene.layout.Region; 43 import javafx.scene.paint.*; 44 import javafx.scene.text.Font; 45 import javafx.scene.text.Text; 46 import javafx.scene.text.TextAlignment; 47 48 /** 49 * This is used in place of CSS because the CSS processing was just too slow. 50 */ 51 public class CalculatorTheme { 52 private static final Color SHADOW_C1 = new Color(1, 1, 1, .07); 53 private static final Color SHADOW_C2 = new Color(1, 1, 1, .7); 54 private static final Color SHADOW_C3 = new Color(1, 1, 1, .75); 55 56 private static final Color DARK_TEXT_COLOR = Color.BLACK; 57 private static final Color MID_TEXT_COLOR = Color.web("#333"); 58 private static final Color LIGHT_TEXT_COLOR = Color.WHITE; 59 private static final Color FOCUS_COLOR = Color.web("#039ED3"); 60 private static final Color FAINT_FOCUS_COLOR = Color.web("#039ED322"); 61 62 private static final Color BASE = Color.web("#ececec"); 63 private static final Color LIGHT_BASE = Color.web("#e4e4e4"); 64 private static final Color DARK_BASE = Color.web("#3d4148"); 65 private static final Color BLUE_BASE = Color.web("#237eb8"); 66 private static final Color ORANGE_BASE = Color.web("#e35f15"); 67 68 private static final Font FONT = Fonts.dosisSemiBold(50); 69 private static final Font DARK_FONT = Fonts.dosisSemiBold(65); 70 71 private static final Insets DARK_PADDING = new Insets(-7, 0, 7, 0); 72 private static final Insets BLUE_PADDING = new Insets(-4, 0, 4, 0); 73 74 private static final Insets SHADOW_INSETS = new Insets(0, 0, -1, 0); 75 private static final Insets OUTER_INSETS = Insets.EMPTY; 76 private static final Insets INNER_INSETS = new Insets(1); 77 private static final Insets BODY_INSETS = new Insets(2); 78 79 private static final CornerRadii SHADOW_RADII = new CornerRadii(3); 80 private static final CornerRadii OUTER_RADII = SHADOW_RADII; 81 private static final CornerRadii INNER_RADII = new CornerRadii(2); 82 private static final CornerRadii BODY_RADII = new CornerRadii(1); 83 84 public static final Background LIGHT_BUTTON_BACKGROUND = new Background( 85 new BackgroundFill(computeShadowHighlight(LIGHT_BASE), SHADOW_RADII, SHADOW_INSETS), 86 new BackgroundFill(computeOuterBorder(LIGHT_BASE), OUTER_RADII, OUTER_INSETS), 87 new BackgroundFill(computeInnerBorder(LIGHT_BASE), INNER_RADII, INNER_INSETS), 88 new BackgroundFill(computeBodyColor(LIGHT_BASE), BODY_RADII, BODY_INSETS) 89 ); 90 public static final Background DARK_BUTTON_BACKGROUND = new Background( 91 new BackgroundFill(computeShadowHighlight(DARK_BASE), SHADOW_RADII, SHADOW_INSETS), 92 new BackgroundFill(computeOuterBorder(DARK_BASE), OUTER_RADII, OUTER_INSETS), 93 new BackgroundFill(computeInnerBorder(DARK_BASE), INNER_RADII, INNER_INSETS), 94 new BackgroundFill(computeBodyColor(DARK_BASE), BODY_RADII, BODY_INSETS) 95 ); 96 public static final Background BLUE_BUTTON_BACKGROUND = new Background( 97 new BackgroundFill(computeShadowHighlight(BLUE_BASE), SHADOW_RADII, SHADOW_INSETS), 98 new BackgroundFill(computeOuterBorder(BLUE_BASE), OUTER_RADII, OUTER_INSETS), 99 new BackgroundFill(computeInnerBorder(BLUE_BASE), INNER_RADII, INNER_INSETS), 100 new BackgroundFill(computeBodyColor(BLUE_BASE), BODY_RADII, BODY_INSETS) 101 ); 102 public static final Background ORANGE_BUTTON_BACKGROUND = new Background( 103 new BackgroundFill(computeShadowHighlight(ORANGE_BASE), SHADOW_RADII, SHADOW_INSETS), 104 new BackgroundFill(computeOuterBorder(ORANGE_BASE), OUTER_RADII, OUTER_INSETS), 105 new BackgroundFill(computeInnerBorder(ORANGE_BASE), INNER_RADII, INNER_INSETS), 106 new BackgroundFill(computeBodyColor(ORANGE_BASE), BODY_RADII, BODY_INSETS) 107 ); 108 109 private static final Paint LIGHT_BUTTON_FILL = computeTextFillColor(LIGHT_BASE); 110 private static final Paint DARK_BUTTON_FILL = computeTextFillColor(DARK_BASE); 111 private static final Paint BLUE_BUTTON_FILL = computeTextFillColor(BLUE_BASE); 112 private static final Paint ORANGE_BUTTON_FILL = computeTextFillColor(ORANGE_BASE); 113 114 115 private static final Color LIGHT_BASE__PRESSED = Utils.deriveColor(LIGHT_BASE, -.06); 116 private static final Color DARK_BASE__PRESSED = Utils.deriveColor(DARK_BASE, -.06); 117 private static final Color BLUE_BASE__PRESSED = Utils.deriveColor(BLUE_BASE, -.06); 118 private static final Color ORANGE_BASE__PRESSED = Utils.deriveColor(ORANGE_BASE, -.06); 119 120 public static final Background LIGHT_BUTTON_BACKGROUND__PRESSED = new Background( 121 new BackgroundFill(computeShadowHighlight(LIGHT_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 122 new BackgroundFill(computeOuterBorder(LIGHT_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 123 new BackgroundFill(computeInnerBorder(LIGHT_BASE__PRESSED), INNER_RADII, INNER_INSETS), 124 new BackgroundFill(computeBodyColor(LIGHT_BASE__PRESSED), BODY_RADII, BODY_INSETS) 125 ); 126 public static final Background DARK_BUTTON_BACKGROUND__PRESSED = new Background( 127 new BackgroundFill(computeShadowHighlight(DARK_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 128 new BackgroundFill(computeOuterBorder(DARK_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 129 new BackgroundFill(computeInnerBorder(DARK_BASE__PRESSED), INNER_RADII, INNER_INSETS), 130 new BackgroundFill(computeBodyColor(DARK_BASE__PRESSED), BODY_RADII, BODY_INSETS) 131 ); 132 public static final Background BLUE_BUTTON_BACKGROUND__PRESSED = new Background( 133 new BackgroundFill(computeShadowHighlight(BLUE_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 134 new BackgroundFill(computeOuterBorder(BLUE_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 135 new BackgroundFill(computeInnerBorder(BLUE_BASE__PRESSED), INNER_RADII, INNER_INSETS), 136 new BackgroundFill(computeBodyColor(BLUE_BASE__PRESSED), BODY_RADII, BODY_INSETS) 137 ); 138 public static final Background ORANGE_BUTTON_BACKGROUND__PRESSED = new Background( 139 new BackgroundFill(computeShadowHighlight(ORANGE_BASE__PRESSED), SHADOW_RADII, SHADOW_INSETS), 140 new BackgroundFill(computeOuterBorder(ORANGE_BASE__PRESSED), OUTER_RADII, OUTER_INSETS), 141 new BackgroundFill(computeInnerBorder(ORANGE_BASE__PRESSED), INNER_RADII, INNER_INSETS), 142 new BackgroundFill(computeBodyColor(ORANGE_BASE__PRESSED), BODY_RADII, BODY_INSETS) 143 ); 144 145 public static void styleLightButton(Button button) { 146 styleButton(button, LIGHT_BUTTON_BACKGROUND, LIGHT_BUTTON_BACKGROUND__PRESSED, LIGHT_BUTTON_FILL); 147 } 148 149 public static void styleDarkButton(Button button) { 150 styleButton(button, DARK_BUTTON_BACKGROUND, DARK_BUTTON_BACKGROUND__PRESSED, DARK_BUTTON_FILL); 151 button.setFont(DARK_FONT); 152 button.setPadding(DARK_PADDING); 153 } 154 155 public static void styleBlueButton(Button button) { 156 styleButton(button, BLUE_BUTTON_BACKGROUND, BLUE_BUTTON_BACKGROUND__PRESSED, BLUE_BUTTON_FILL); 157 button.setPadding(BLUE_PADDING); 158 } 159 160 public static void styleOrangeButton(Button button) { 161 styleButton(button, ORANGE_BUTTON_BACKGROUND, ORANGE_BUTTON_BACKGROUND__PRESSED, ORANGE_BUTTON_FILL); 162 button.setTextFill(Color.WHITE); 163 } 164 165 public static void styleEqualsButton(Button button) { 166 styleOrangeButton(button); 167 button.setFont(DARK_FONT); 168 button.setPadding(DARK_PADDING); 169 } 170 171 public static void styleBackground(Region parent) { 172 parent.setBackground(new Background(new BackgroundFill(BASE, null, null))); 173 } 174 175 public static void styleTextField(TextField textField) { 176 final Color controlInnerBackground = Utils.deriveColor(BASE, .8); 177 final Color textInnerColor = Utils.ladder(controlInnerBackground, new Stop[]{ 178 new Stop(.45, LIGHT_TEXT_COLOR), 179 new Stop(.46, LIGHT_TEXT_COLOR), 180 new Stop(.59, DARK_TEXT_COLOR), 181 new Stop(.60, MID_TEXT_COLOR) 182 }); 183 final Color textBoxBorder = Utils.ladder(BASE, new Stop[] { 184 new Stop(.1, Color.BLACK), 185 new Stop(.3, Utils.deriveColor(BASE, -.15)) 186 }); 187 final Paint backgroundColor = new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 188 new Stop(0, Utils.deriveColor(textBoxBorder, -.1)), 189 new Stop(1, textBoxBorder) 190 ); 191 final Paint backgroundColor2 = new LinearGradient(0, 0, 0, 5, false, CycleMethod.NO_CYCLE, 192 new Stop(0, Utils.deriveColor(controlInnerBackground, -.09)), 193 new Stop(1, controlInnerBackground) 194 ); 195 textField.setFont(FONT); 196 textField.setBackground(new Background( 197 new BackgroundFill(backgroundColor, new CornerRadii(3), Insets.EMPTY), 198 new BackgroundFill(backgroundColor2, new CornerRadii(2), new Insets(1)) 199 )); 200 textField.setPadding(new Insets(.333333 * FONT.getSize(), .583 * FONT.getSize(), .333333 * FONT.getSize(), .583 * FONT.getSize())); 201 TextFieldSkin skin = new TextFieldSkin(textField) { 202 { 203 textFill.set(textInnerColor); 204 } 205 }; 206 textField.setSkin(skin); 207 } 208 209 private static void styleButton(final Button button, final Background background, final Background pressed, Paint textFill) { 210 button.setBackground(background); 211 button.setTextFill(textFill); 212 button.setAlignment(Pos.CENTER); 213 button.setTextAlignment(TextAlignment.CENTER); 214 button.setContentDisplay(ContentDisplay.LEFT); 215 button.setPadding(Insets.EMPTY); 216 button.setFont(FONT); 217 button.pressedProperty().addListener(new ChangeListener<Boolean>() { 218 @Override 219 public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 220 if (newValue) { 221 button.setBackground(pressed); 222 } else { 223 button.setBackground(background); 224 } 225 } 226 }); 227 ButtonSkin skin = new ButtonSkin(button); 228 button.setSkin(skin); 229 // Workaround a bug 230 ((Text)skin.getChildren().get(0)).fillProperty().bind(button.textFillProperty()); 231 } 232 233 /* 234 .button:hover { 235 -fx-color: -fx-hover-base; 236 } 237 -fx-hover-base: ladder( 238 -fx-base, 239 derive(-fx-base,20%) 20%, 240 derive(-fx-base,30%) 35%, 241 derive(-fx-base,40%) 50% 242 ); 243 */ 244 245 /* 246 .button:focused { 247 -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color, -fx-faint-focus-color, -fx-body-color; 248 -fx-background-insets: -0.2, 1, 2, -1.4, 2.6; 249 -fx-background-radius: 3, 2, 1, 4, 1; 250 } 251 */ 252 253 /* 254 .background { 255 -fx-background-color: -fx-background; 256 } 257 */ 258 259 private static Color deriveBackground(Color base) { 260 return Utils.deriveColor(base, .264); 261 } 262 263 private static Paint computeShadowHighlight(Color base) { 264 /* 265 -fx-shadow-highlight-color: ladder( 266 -fx-background, 267 rgba(255,255,255,0.07) 0%, 268 rgba(255,255,255,0.07) 20%, 269 rgba(255,255,255,0.07) 70%, 270 rgba(255,255,255,0.7) 90%, 271 rgba(255,255,255,0.75) 100% 272 ); 273 */ 274 return Utils.ladder(deriveBackground(base), new Stop[] { 275 new Stop(0, SHADOW_C1), 276 new Stop(.2, SHADOW_C1), 277 new Stop(.7, SHADOW_C1), 278 new Stop(.9, SHADOW_C2), 279 new Stop(1, SHADOW_C3) 280 }); 281 } 282 283 private static Paint computeOuterBorder(Color color) { 284 /* derive(-fx-color,-23%); */ 285 return Utils.deriveColor(color, -.23); 286 } 287 288 private static Paint computeInnerBorder(Color color) { 289 /* 290 -fx-inner-border: linear-gradient(to bottom, 291 ladder( 292 -fx-color, 293 derive(-fx-color,30%) 0%, 294 derive(-fx-color,20%) 40%, 295 derive(-fx-color,25%) 60%, 296 derive(-fx-color,55%) 80%, 297 derive(-fx-color,55%) 90%, 298 derive(-fx-color,75%) 100% 299 ), 300 ladder( 301 -fx-color, 302 derive(-fx-color,20%) 0%, 303 derive(-fx-color,10%) 20%, 304 derive(-fx-color,5%) 40%, 305 derive(-fx-color,-2%) 60%, 306 derive(-fx-color,-5%) 100% 307 )); 308 */ 309 return new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 310 new Stop(0, Utils.ladder(color, new Stop[] { 311 new Stop(0, Utils.deriveColor(color, .3)), 312 new Stop(.4, Utils.deriveColor(color, .2)), 313 new Stop(.6, Utils.deriveColor(color, .25)), 314 new Stop(.8, Utils.deriveColor(color, .55)), 315 new Stop(.9, Utils.deriveColor(color, .55)), 316 new Stop(1, Utils.deriveColor(color, .75)) 317 })), 318 new Stop(1, Utils.ladder(color, new Stop[] { 319 new Stop(0, Utils.deriveColor(color, .2)), 320 new Stop(.2, Utils.deriveColor(color, .1)), 321 new Stop(.4, Utils.deriveColor(color, .05)), 322 new Stop(.6, Utils.deriveColor(color, -.02)), 323 new Stop(1, Utils.deriveColor(color, -.05)) 324 })) 325 ); 326 } 327 328 private static Paint computeBodyColor(Color color) { 329 /* 330 -fx-body-color: linear-gradient(to bottom, 331 ladder( 332 -fx-color, 333 derive(-fx-color,8%) 75%, 334 derive(-fx-color,10%) 80% 335 ), 336 derive(-fx-color,-8%)); 337 */ 338 return new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 339 new Stop(0, Utils.ladder(color, new Stop[] { 340 new Stop(.75, Utils.deriveColor(color, .08)), 341 new Stop(.80, Utils.deriveColor(color, .10)) 342 })), 343 new Stop(1, Utils.deriveColor(color, -.08)) 344 ); 345 346 } 347 348 private static Color computeTextFillColor(Color color) { 349 /* 350 -fx-text-base-color: ladder( 351 -fx-color, 352 -fx-light-text-color 45%, 353 -fx-dark-text-color 46%, 354 -fx-dark-text-color 59%, 355 -fx-mid-text-color 60% 356 ); 357 */ 358 return Utils.ladder(color, new Stop[] { 359 new Stop(.45, LIGHT_TEXT_COLOR), 360 new Stop(.46, LIGHT_TEXT_COLOR), 361 new Stop(.59, DARK_TEXT_COLOR), 362 new Stop(.60, MID_TEXT_COLOR) 363 }); 364 } 365 }