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 }