<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>I realized I worded the TLDR poorly.</p>
    <p>Let me try again:</p>
    <p>TLDR; should styles which use references (like -fx-base used in
      Modena) become AUTHOR level styles if -fx-base is specified in an
      AUTHOR stylesheet?  The act of simply specifying -fx-base in your
      own AUTHOR stylesheet elevates hundreds of styles from Modena to
      AUTHOR level, as if you specified them directly...</p>
    <p>--John<br>
    </p>
    <div class="moz-cite-prefix">On 09/07/2024 02:07, John Hendrikx
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:3179a05e-d73f-f6cd-2e0b-d36e4b0247e7@gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <p>Hi List,</p>
      <p>TLDR; should a CSS reference like -fx-base convert all styles
        that use this value (or derive from it) become AUTHOR level
        styles (higher priority than setters) ?</p>
      <p>Long version:<br>
      </p>
      <p>In JavaFX 21, I did a fix (see #1072) to solve a problem where
        a CSS value could be reset on an unrelated control.</p>
      <p>This happened when the CSS engine encountered a stylable that
        is overridden by the user (with a setter), and decided NOT to
        proceed with the full CSS value calculation (as it could not
        override the user setting if that CSS value had lower
        priority).  However, not proceeding with the calculation meant
        that a "SKIP" was stored in a shared cache which was incorrect. 
        This is because when this "SKIP" is later encountered for an
        unrelated control (the cache entries are shared for controls
        with the same styles at the same level), they could get their
        values reset because they were assumed to be unstyled.</p>
      <p>However, this fix has exposed what seems to be a deeper bug or
        perhaps an unfortunate default:</p>
      <p>JavaFX has a special feature where you can refer to certain
        other styles by using a reference (which is resolved,
        recursively, to a final value).  This does not seem to be a CSS
        standard, but is a feature only FX has.</p>
      <p>It works by saying something like:</p>
      <p>    -fx-base: RED;</p>
      <p>And then using it like this:</p>
      <p>    -fx-text-fill: -fx-base;</p>
      <p>This feature works accross stylesheets of different origins, so
        an AUTHOR stylesheet can specify -fx-base, and when a USER_AGENT
        refers to -fx-base, the value comes from the AUTHOR stylesheet.</p>
      <p>JavaFX then changes the origin of the style to the highest
        priority encountered while resolving the reference.  This means
        that Modena can specify "-fx-text-fill: -fx-base", and when
        "-fx-base" is then part of the AUTHOR style sheet, that ALL
        Modena styles that use -fx-base will be considered AUTHOR level
        styles, as per this comment:</p>
      <div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
        <div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p style="margin:0;"><span style="color:#000000;">                    </span><span style="color:#3f7f5f;">// The origin of this parsed value is the greatest of</span></p><p style="margin:0;"><span style="color:#000000;">                    </span><span style="color:#3f7f5f;">// any of the resolved reference. If a resolved reference</span></p><p style="margin:0;"><span style="color:#000000;">                    </span><span style="color:#3f7f5f;">// comes from an inline style, for example, then the value</span></p><p style="margin:0;"><span style="color:#000000;">                    </span><span style="color:#3f7f5f;">// calculated from the resolved lookup should have inline</span></p><p style="margin:0;"><span style="color:#000000;">                    </span><span style="color:#3f7f5f;">// as its origin. Otherwise, an inline style could be</span></p><p style="margin:0;"><span style="color:#000000;">                    </span><span style="color:#3f7f5f;">// stored in shared cache.</span></p></div>
      </div>
      <p>I feel that this is a really unfortunate choice.  The style
        after all was specified by Modena, only its value came from
        another (higher priority) style sheet.  I think a more logical
        choice would have been to not change the priority at all, unless
        a "-fx-text-fill" is explicitly made part of the AUTHOR
        stylesheet.</p>
      <p>A consequence of this (and which is much more visible after the
        fix) is that creating a Label with a setTextFill(Color.YELLOW)
        in its constructor will only result in a yellow text fill if the
        AUTHOR stylesheet did not override any of the Modena colors
        involved in calculating the Modena -fx-text-fill default. 
        Overriding -fx-base in any way will result in the text fill of
        the label to be overridden (as the reference came from an AUTHOR
        stylesheet, which trumps a setter which is of a lower style
        origin).<br>
      </p>
      <p>The comment also alludes to a potential problem.  If an inline
        style would specify "-fx-base", but would be treated as if it
        came from Modena (USER_AGENT level), then this value could get
        stored in the cache as everything except INLINE styles can be
        cached.  However, I feel that the changing of style origin level
        was then probably done to solve a CACHING problem, instead of
        what made logical sense for users.  If we agree that a resolved
        reference should not change the style origin level, then this
        would need to be addressed, by perhaps marking such a calculated
        value as uncacheable, instead of overloading the meaning of
        style origin.</p>
      <p>I'd like to hear your thoughts, and also how to proceed. 
        JavaFX versions before 21 seemingly allowed overriding reference
        without much consequence because if the user overrode the value
        manually, the cache entry would be set to "SKIP".  Now that this
        is no longer the case, JavaFX more aggressively overrides user
        set values if they happen to use a referenced value.  See code
        below.</p>
      <p>--John</p>
      <div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
        <div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p style="margin:0;">    .root {</p><p style="margin:0;">        -fx-base: #ff0000;</p><p style="margin:0;">    }</p><p style="margin:0;">
</p><div style="background-color:#ffffff;padding:0px 0px 0px 2px;"><div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">package</span><span style="color:#000000;"> app;</span></p><p style="margin:0;"><span style="color:#000000;">    </span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">import</span><span style="color:#000000;"> javafx.application.Application;</span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">import</span><span style="color:#000000;"> javafx.scene.Scene;</span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">import</span><span style="color:#000000;"> javafx.scene.control.Label;</span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">import</span><span style="color:#000000;"> javafx.scene.paint.Color;</span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">import</span><span style="color:#000000;"> javafx.stage.Stage;</span></p><p style="margin:0;"><span style="color:#000000;">    </span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">public</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">class</span><span style="color:#000000;"> TestApp </span><span style="color:#0000a0;font-weight:bold;">extends</span><span style="color:#000000;"> Application {</span></p><p style="margin:0;"><span style="color:#000000;">      </span><span style="color:#0000a0;font-weight:bold;">public</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">static</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">void</span><span style="color:#000000;"> main(String[] args) {</span></p><p style="margin:0;"><span style="color:#000000;">        </span><span style="color:#000000;font-style:italic;">launch</span><span style="color:#000000;">(args);</span></p><p style="margin:0;"><span style="color:#000000;">      }</span></p><p style="margin:0;"><span style="color:#000000;">    </span></p><p style="margin:0;"><span style="color:#000000;">      </span><span style="color:#646464;">@</span><span style="color:#646464;">Override</span></p><p style="margin:0;"><span style="color:#000000;">      </span><span style="color:#0000a0;font-weight:bold;">public</span><span style="color:#000000;"> </span><span style="color:#0000a0;font-weight:bold;">void</span><span style="color:#000000;"> start(Stage primaryStage) {</span></p><p style="margin:0;"><span style="color:#000000;">        Scene scene = </span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> Scene(</span><span style="color:#0000a0;font-weight:bold;">new</span><span style="color:#000000;"> MyLabel());</span></p><p style="margin:0;"><span style="color:#000000;">        </span><span style="color:#3f7f5f;">// See the difference with/without -fx-base in the </span><span style="color:#3f7f5f;text-decoration:underline;text-decoration-color:#ff8040;text-decoration-style:wavy;">stylesheet</span></p><p style="margin:0;"><span style="color:#000000;">        scene.getStylesheets().add(TestApp.</span><span style="color:#0000a0;font-weight:bold;">class</span><span style="color:#000000;">.getResource(</span><span style="color:#2a00ff;">"/style.css"</span><span style="color:#000000;">).toExternalForm());</span></p><p style="margin:0;"><span style="color:#000000;">        primaryStage.setScene(scene);</span></p><p style="margin:0;"><span style="color:#000000;">        primaryStage.show();</span></p><p style="margin:0;"><span style="color:#000000;">      }</span></p><p style="margin:0;"><span style="color:#000000;">    }</span></p><p style="margin:0;"><span style="color:#000000;">    </span></p><p style="margin:0;"><span style="color:#000000;">    </span><span style="color:#0000a0;font-weight:bold;">class</span><span style="color:#000000;"> MyLabel </span><span style="color:#0000a0;font-weight:bold;">extends</span><span style="color:#000000;"> Label {</span></p><p style="margin:0;"><span style="color:#000000;">      </span><span style="color:#0000a0;font-weight:bold;">public</span><span style="color:#000000;"> MyLabel() {</span></p><p style="margin:0;"><span style="color:#000000;">        setTextFill(Color.</span><span style="color:#0000c0;">YELLOW</span><span style="color:#000000;">);</span></p><p style="margin:0;"><span style="color:#000000;">        setText(</span><span style="color:#2a00ff;">"Hello world"</span><span style="color:#000000;">);</span></p><p style="margin:0;"><span style="color:#000000;">      }</span></p><p style="margin:0;"><span style="color:#000000;">    }</span></p></div></div><p style="margin:0;">
</p></div>
      </div>
      <p><br>
      </p>
    </blockquote>
  </body>
</html>