Instances of this class correspond to states of Swing core and custom controls. This class provides a number of predefined static instances to cover most action-based controls such as buttons, check boxes and menu items. In addition, application code can define custom component states that create fine grained mapping between arbitrary states of controls and specific color scheme bundles in custom skins.
Each component state is defined by two arrays of component state facets (available in {@link ComponentStateFacet} class). The first array specifiesthe facets that are on, and the second array specifies the facets that are off. For example, when a selected toggle button is pressed, it transitions to {@link #PRESSED_SELECTED} state. This state has{@link ComponentStateFacet#ENABLE}, {@link ComponentStateFacet#SELECTION} and{@link ComponentStateFacet#PRESS} as its on facets. If a selectedtoggle button is disabled, it has {@link ComponentStateFacet#SELECTION} inits on facets and {@link ComponentStateFacet#ENABLE} in its offfacets.
The {@link ComponentStateFacet} class defines a number of core facets. The{@link ComponentStateFacet#ENABLE} facet is universal - it is relevant forall Swing controls. Other facets apply to a wider range of controls. For example, {@link ComponentStateFacet#ROLLOVER} facet applies to all controlsthat can show rollover effects - including buttons, menu items, comboboxes, sliders, scrollbars and many more. Some facets apply to a very narrow range of controls. For exaple, {@link ComponentStateFacet#EDITABLE} is onlyrelevant for editable controls, such as text components, editable comboboxes or spinners.
The static instances of {@link ComponentState} defined in this class do notaim to cover all possible combinations of on and off facets. In addition to making this class to unwieldy, it is not possible to do since application code can define its own facets. Instead, Substance provides three ways to fine-tune the mapping between the component states and the color schemes used to paint the components.
- When the skin is queried for the color scheme that matches the specific component state - let's say {@link ComponentState#PRESSED_SELECTED} - theskinning layer first looks for the exact state (as passed to {@link SubstanceColorSchemeBundle#registerColorScheme(SubstanceColorScheme,ColorSchemeAssociationKind,ComponentState)}or similar APIs). If the exact match is found, it is used. If there is no exact match, the skinning layer will look at all color schemes registered for the specific color scheme association kind in the matching color scheme bundle. The decision is made based on how "close" the registered component state is to the component state of the currently painted component. For example, {@link ComponentState#PRESSED_SELECTED} is a better match for{@link ComponentState#PRESSED_UNSELECTED} than{@link ComponentState#ROLLOVER_SELECTED} - since the{@link ComponentStateFacet#PRESS} has more weight than the{@link ComponentStateFacet#ROLLOVER} in the decision process. The skinninglayer will choose the "closest" registered component state that is sufficiently close. For example, {@link ComponentState#DISABLED_SELECTED}will never be chosen for {@link ComponentState#SELECTED}, even if there are no other registered component states. This way the application code can register a few color schemes in the specific bundle, and have all other states "fall back" to the smaller subset of states.
- Facets such as {@link ComponentStateFacet#DETERMINATE} or{@link ComponentStateFacet#EDITABLE} are relevant only for a small subset ofcontrols. In order to simplify the API signature of {@link ComponentState}, these facets are not part of any of the predefined static states in this class. Instead, they are used internally in the matching UI delegates (such as for progress bar or text components) to find the best match among all the registered states of the current skin. The specific skin can define its own {@link ComponentState} instances that use these facets. For example,{@link NebulaSkin} defines a number of component states that use the{@link ComponentStateFacet#DETERMINATE} facet, and maps the matching colorschemes. At runtime, the procedure described in the previous item will match the state of the specific progress bar to the states defined in this skin, and use the matching color schemes.
- Custom application components may have facets that do not directly map to the core facets defined in the {@link ComponentStateFacet} class. In thiscase, the application code can create its own facet instances, and its own component states that use those facets in the on and off lists. Part of the custom code will be in the UI delegates that compute the current state of the custom component using the new facets. Other part of the custom code will be in the skin definition that maps the component states defined with the new facets to the specific color schemes.
Note that you do not have to create explicit dependency between custom component states used in the skin definition and custom component states used in the painting routines (in the UI delegates). In fact, the custom component states defined in the Substance UI delegate for progress bar are not accessible to the application code. The recommended way to separate the skin definition from the model lookups in the painting is:
- The skin definition defines a sufficiently broad set of custom component states that use the new facets. Note that you do not have to create a custom state for every possible permutation of new facets (along with the relevant core facets). A well defined set of component states will provide a good fallback state for every relevant permutation of facets, keeping the skin definition small and manageable.
- The UI delegate that queries the component model will use accurate component states that account for all the relevant on and off facets - including the core facets defined in the {@link ComponentStateFacet} class.When this (perhaps elaborate) state is passed to {@link SubstanceColorSchemeBundle#getColorScheme(ColorSchemeAssociationKind,ComponentState)}API, the the procedure described above will match the this state to one of the "base" states defined in your skin, and use the matching color scheme.
Note that the matching algorithm only looks at the facets in the on and off lists, and ignores the component state name. This allows you to create a broad component state in your skin, and a number of narrow component states during the painting - and have the Substance skinning layer find the best match.
When the matching algorithm cannot find a sufficiently close match, the skinning layer will fall back on one of the three base color schemes passed to the {@link SubstanceColorSchemeBundle#SubstanceColorSchemeBundle(SubstanceColorScheme,SubstanceColorScheme,SubstanceColorScheme)}constructor. States with {@link ComponentStateFacet#ENABLE} in their off listwill fall back to the disabled color scheme. The {@link ComponentState#ENABLED} will fall back to the enabled color scheme.The rest of the states will fall back to the active color scheme. To change the fallback behavior pass a non-null fallback color scheme to the {@link ComponentState#ComponentState(String,ComponentState,ComponentStateFacet[],ComponentStateFacet[])}constructor as the second parameter.
@author Kirill Grouchnikov