Greg,

I just took a look at a small part of your implementation and I see that our approaches are fundamentally different. You parse the stylesheet, set static properties on the Component class or some subclass thereof, and then use that information somewhere along the line to perform the styling. I parse the stylesheet into a "stylesheet document" and apply it to a component hierarchy, presumably after it's been constructed by a serializer.

At the moment I don't have time to explain why I like my approach, but I do think it supports a richer stylesheet language.

Where/when do you set the styles in your implementation?

Cheers,

Michael


On Sun, 18 Jul 2010, Greg Brown wrote:

Hi all,

Earlier today I committed a change that adds named and typed style support to WTK. Callers can either associate a set of styles 
with a "name" (a string) or a "type" (a class that extends Component). This is consistent with the styling 
support provided by Flex, which supports CSS type selectors and class selectors but not a combination of the two (e.g. 
"Button" and ".foo" but not "Button.foo").

Named and typed styles are specified via the static "namedStyles" and "typedStyles" collections of 
Component. When a component is instantiated, setSkin() walks the component's class hierarchy and applies any matching 
classes. When the "styleName" or "styleNames" property of a component is set, any matching named 
style(s) are applied. This is also consistent with Flex, which uses a static StyleManager singleton to manage named 
styles. Note that Pivot supports a list of style names, whereas Flex only supports a single style name.

An application can populate the named and typed style maps manually, or it can be done 
automatically at application startup via the "stylesheet" startup property. 
This property contains the path to the stylesheet that will be used to style the 
application. This is slightly different from how it is done in Flex, where a caller can 
specify a stylesheet in MXML. This is not possible in Pivot because Pivot applications 
are not defined in markup; they are classes that implement the Application interface. 
However, separating the stylesheet from the application may actually be a better 
approach, since it allows the application to be instantiated with an arbitrary 
stylesheet, rather than creating a hard association with a single stylesheet.

Another problem with the Flex approach is that defining a stylesheet anywhere other 
than in the <mx:Application> tag can have unexpected side effects; see note:

http://livedocs.adobe.com/flex/3/html/help.html?content=styles_05.html#165272

I'm guessing this is because applying the <mx:Style> tag modifies the state of 
the StyleManager singleton. As a result, even though it looks like the styles would 
only apply to the current document, they would actually affect all documents in the 
application. Allowing stylesheets to be applied only at the application level 
eliminates this ambiguity.

You can see an example of the new styling support here:

http://svn.apache.org/repos/asf/pivot/trunk/demos/src/org/apache/pivot/demos/styles/

I haven't posted a live demo, but it should be pretty obvious from the code 
what is going on: styles.json defines a number of typed and named styles that 
are referred to by name and type in stylesheet_demo.bxml.

Note that none of these changes preclude the use of CSS to define styles. 
However, I'm now leaning away from advocating a wholesale migration to CSS in 
Pivot. While thinking through this solution, I remembered that, even though 
styles are defined in CSS in HTML and in Flex, anytime you want to modify them 
programmatically, you need to use the JSON version of the style names. Given 
this, it is arguably better to use JSON throughout the framework for style 
specification, so the developer does not need to switch between two different 
syntaxes for managing styles.

I'm happy with how this turned out, and I think it represents a considerable 
improvement over the previous two approaches I have prototyped (applying 
stylesheets via BXML includes and applying typed styles via JSON-based style 
classes). This change replaces the latter, but it actually does not replace the 
former. In fact, there are still occasions when you may want to use an include 
to specify styles. For example, the include approach supports dynamic binding 
to style values, while the named style approach does not. This was intentional 
- the primary purpose of named styles is to allow the details of an 
application's appearance to be abstracted from the structure of the UI itself. 
Most of the time, named styles are applied only once, when a component is 
initially created. Dynamic re-styling is almost a completely separate use case. 
Fortunately, the current implementation provides an easy way for developers to 
do both.

Thanks to all involved for your input. Please let me know if you have any 
questions.

Greg


On Jul 17, 2010, at 9:53 AM, Greg Brown wrote:

If you haven't read through this thoroughly yet, please hold off. I have been 
working through the design and have discovered a number of issues. I am 
currently revising the approach and will send an updated email.

On Jul 16, 2010, at 11:35 AM, Greg Brown wrote:

This email is a bit long but I think it represents a good way to move the 
styling discussion forward. Please read it if you are interested in this 
discussion.

I have been thinking about a way that we could support CSS either in addition 
to or instead of JSON for styling. My original proposal for adding named style 
support was something along these lines:

<Window styleClasses="@my_styles.json"
  xmlns:bxml="http://pivot.apache.org/bxml";
  xmlns="org.apache.pivot.wtk">
  <BoxPane>
      <Label styleClassNames="a, b, c" styles="{color:green; 
font:Arial-BOLD-24}"/>
  </BoxPane>
</Window>

The "my_styles.json" file defines the (untyped) style classes "a", "b", and "c". These would be 
applied when the "styleClassNames" attribute is processed. The values in the "styles" attribute would then override any 
of the styles defined by the style classes.

However, this approach didn't work for two reasons:

1) Attributes aren't processed until the closing tag. This is by design - consider a CardPane with 
a "selectedIndex" attribute. This value can't be set until the card pane's children have 
been added. The implication here is that the named styles specified by Window's 
"styleClasses" attribute won't be loaded by the time the Label is created, so the named 
style classes won't be found.

2) Child elements aren't added to the parent until the closing tag. So, even if 
#1 is solved, Label won't be able to walk up the tree to find the named styles 
because BoxPane wouldn't have been added to the Window yet.

However, I believe I have a solution to both of these issues. I have already 
modified BXMLSerializer to add child elements in the start tag, which resolves 
#2. The resolution to #1 could be an annotation that specifies when an 
attribute should be processed (e.g. @PostApply). If this attribute is not 
specified, the attribute would be pre-applied (i.e. processed in the start tag).

Unfortunately, as we have discussed, another issue with this approach is that 
JSON doesn't support namespaces. So it would be cumbersome to try to support 
typed selectors in JSON. Typed selectors could be specified in WTKX, though:

<Window
  xmlns:bxml="http://pivot.apache.org/bxml";
  xmlns="org.apache.pivot.wtk">
  <styleClasses>
      <StyleClass type="org.apache.pivot.wtk.Label" name="a" 
styles="{foo:'bar'}"/>
  </styleClasses>

  <BoxPane>
      <Label styleClassNames="a, b, c" styles="{color:green; 
font:Arial-BOLD-24}"/>
  </BoxPane>
</Window>

Additionally, given CSS 3's proposed support for namespaces, it would become 
possible to declare both typed and untyped selectors in an external CSS file. 
WTK could also be updated to use inline CSS for local style specifications vs. 
JSON, which would be slightly less verbose (no opening and closing curly 
braces).

<Window styleClasses="@my_styles.css"
  xmlns:bxml="http://pivot.apache.org/bxml";
  xmlns="org.apache.pivot.wtk">
  <BoxPane>
      <Label styleClassNames="a, b, c" styles="color:green; 
font:Arial-BOLD-24"/>
  </BoxPane>
</Window>

I think this approach would be much more intuitive than the current one. It 
would also be more consistent, since all styling would be specified via CSS, 
rather than by a combination of classpath, attribute, and page-level variables.

The changes required to support this feature include:

1) BXMLSerializer modifications discussed above (trivial, and nearly complete)
2) Addition of a StyleClassSequence to Container (easy, but slightly 
time-consuming)
3) Addition of serialization support for loading stylesheets and processing 
inline CSS

#3 is obviously the most challenging. It will require implementing a new 
serializer capable of processing CSS stylesheets:

StylesheetSerializer : Serializer<StyleClass>

An instance of this class would be used to process the "styleClasses" attribute. It 
would probably also be used to process inline style declarations, perhaps via a 
readStyleDeclaration(InputStream):Map<String, ?> method.

I don't think that implementing such a class will be very difficult. However, 
as I mentioned earlier, I don't want to introduce a dependency on a 3rd party 
library to support it. I'm not opposed to porting an existing implementation 
that has a compatible license and including it in the platform, though. No need 
to build it from scratch if there is already a good one we can use.

One downside to moving to CSS for styling is the potential loss of support for 
font encodings such as this:

font: {bold:true}

I don't think we'd want to mix this kind of JSON-oriented styling with CSS. 
Instead, we'd want to support the CSS syntax:

font-weight: bold

This is doable - it just means that any skin that supports a "font" style 
should also define setFontFamily(), setFontSize(), and setFontWeight() methods.

So, here is what I propose: I will make the necessary changes to BXMLSerializer 
and Container that are required to support this. I will leave the existing 
JSON-based style support in place for now (though I will probably yank the 
classpath-based type selectors, since they will no longer be necessary). CSS 
won't be supported, but developers will be able to define and apply style 
classes in BXML as shown above.

I would very much like to see support for CSS added, though. If anyone is 
interested in working on the StylesheetSerializer class, please let me know. I 
think it would make a great addition to the platform, and I would be highly 
inclined to drop support for JSON styling in favor of CSS if this functionality 
becomes available.

Please let me know what you think.

Thanks,
Greg



Reply via email to