Menu

Pattern for Exposing Component Behaviors

SourceForge Editorial Staff

Pattern for Exposing Component Behaviors

Problem Description

As we add behaviors to our components, like 'smoothScrolling' to ScrollBar/Scroller and 'resizeThumb' to ScrollBar, we need to know how/where to expose these behaviors so that developers can change the default or per-instance behavior. Should they be styles? Should they be properties? If properties, are they backed by some static property so that developers can easily change the default application behavior?

Decision

Complete this section when a decision has been made and approved

Decision Criteria

A good decision would be: - understandable, so that developers would generally know how to expose behaviors and didn't need to cast around for input when the issues arise - easily doable: we're not looking for core infrastructure changes here, but just a policy decision, and probably some scrubbing of current behaviors as appropriate

Proposed Solution(s):

1) Behaviors as Styles

There is already precedent for this in Halo. For example, Slider exposed slideDuration and slideEasingFunction as styles:

<a href="Style%28name%3D%26quot%3BslideDuration%26quot%3B%2C%20type%3D%26quot%3BNumber%26quot%3B%2C%20format%3D%26quot%3BTime%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="slideDuration", type="Number", format="Time", inherit="no")</a>
<a href="Style%28name%3D%26quot%3BslideEasingFunction%26quot%3B%2C%20type%3D%26quot%3BFunction%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="slideEasingFunction", type="Function", inherit="no")</a>

This approach allows the developer to specify a global default that is picked up on the fly by existing objects. It also allows developers to target specific instances as necessary.
For example, we could add styles like the above, and smoothScrolling, and thumbSize to particular classes like Slider and ScrollBar. Then we could add default values to defaults.css. Then the classes depending on these behaviors could dynamically query the styles when necessary. Finally, developers wishing for different behavior could write CSS code that changed the value globally or per-instance .

Usage:
To globally change a style, a developer could write the following CSS:

global {
    smoothScrolling: false;
}

or

ScrollBar {
    smoothScrolling: false;
}

To change the style for scrollbars inside of all TextArea components, they could write this, using descendent selectors:

TextArea ScrollBar{
    smoothScrolling: false;
}

2) Behaviors as Properties

It is straightforward to expose behaviors as properties instead of styles. For example, smoothScrolling is currently a Boolean property on ScrollBar. The class looks at this property's value whenever it needs to perform a scrolling operation and behaves accordingly.

It can be tricky for developers to get a handle to the specific ScrollBar instances they need. For example, developers create TextAreas, which then create Scrollers, which then, internally, use ScrollBars. For a developer to change a property on a ScrollBar instance, they need to burrow all the way down to get ahold of that property. It is probably more useful to expose some way of setting the property default, so we propose a static flag on some class (ScrollBar, in the case of smoothScrolling), which acts as the default value for the instance property when that instance is created.

A developer wishing for non-default behavior could simply set the static default value and then expect all instances to pick that value up. They could, alternatively, get ahold of the proper instances and set those values directly. They could also, in the manner of spark developers, create a custom skin that set the property. In some cases, it might make sense to expose the property through other components to make it more visible to the developer. For example, the smoothScrolling behavior is exposed on both ScrollBar and Scroller, so a skin developer could set the property directly on the Scroller to get that instance to have non-default behavior.

2b) Exposing static properties through MXML

Currently, the only way to set static properties is through ActionScript code. Should we make it possible to do this through MXML?

We recommend approahc #1: Behaviors as Styles. This approach is pretty standardized in Halo components and provides the per-instance and global value-setting facilities we want. Going the property route would really be a new pattern (especially with the static-property approach) and might eventually require tooling work; why create a new approach when a perfectly adequate one exists?

Notes:

Meeting 5/4/09

To answer a question in the original writeup, styles can be used to target specific instances, either through inline styles (styles exposed as attributes on a component) or through advanced CSS.

Styles support dynamic behavior changes more easily than the property approach - calling getStyle() will always return the latest value, as opposed to using a static property and caching the instance property. On the other hand, it is probably not a common use case to want to change the behavior on the fly for an instantiated object.

Behaviors in CSS in the HTML world: originally, behaviors in CSS were somewhat controversial. But they have been available in various forms from browser makers for some time and seem less controversial now.

Decision: let's go with styles, not properties.

Where do we expose the styles?

  • At the lowest level that makes sense. For example, Scrollbar will advertise the smoothScrolling style.
  • Instead of proxying styles through other related components (e.g., Scroller in addition to ScrollBar), we will just have the properties available on the components that make sense and use the capabilities of CSS for allowing developers to set the properties. For example, even though the developer may not have a handle to the ScrollBar inside the Scroller which is inside the TextArea that they are creating, they can (through advanced CSS) tell the TextArea to disable smoothScrolling on any ScrollBar components inside of the TextArea.

Where do we set the global defaults?

  • in the defaults.css file
  • in the Global selector

Avoid setting styles on class-specific selectors, because it makes it difficult for developers to figure out what the defaults are and what the behaviors will or should be. Putting everything in global makes it more obvious.

As background, there is a pattern for spark components now to put everything visual in the Global type selector.

Note on metadata: this is only needed in situations where developers might want to set the style as an inline attribute.

Decisions:

  • Use styles, not properties
  • Declare defaults in Global type selector in defaults.css
  • No hoisting: put the properties in the lowest components or base classes where they make sense
  • Dynamic behavior by default (call getStyle() instead of caching the value)
    • Note that some situations may require overriding styleChanged() to pick up the value if and when it changes. This is not the case for smoothScrolling, since we would call getStyle() and get the dynamic value every time an operation was requested that needed to know whether to animate it or not. The thumbResize property may require the override, however, since changing the style might also change the size of the thumb as a result.
  • things to do:
    • fix scrollbar properties ("smoothScrolling" property/static-property becomes style instead)
    • check on whether thumbResize needs to be changed
    • scrub existing spark components to fix any other behavior-related properties

Note: this change is being made only for spark components; we are not changing the way we do things for Halo components.


Related

Wiki: Flex 4

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.