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 }