Copyright (C) 2003 Hotsprings Inc.
Distributed under the GPL
Location: www.Opensprings.org
Description:
This file is common to all the demos. It builds the
skin needed by the demo apps.
The SkinBuilder class loads the needed images, and
turns them into the desired skin. The generated application skin
contains all skins needed by the views in the app. The generated skin
(common to all the demos) contains more skins that are required by
any particular demo. This is a waste of resources but since these are
just sample applications we considered it is better not to let the
skin creation interfere with the actual code of the app.
The images are loaded from a folder called
"Skin.rsrc". This folder is located within any of the
parent folders of the application launched. This allows various apps
to be deployed in separate folders but grouped together inside the
same parent folder. By placing the skin resources within that same
parent folder we allow for the sharing of the skin resources.
To load images we use an ImagePool object. ImagePool
is an agent with the ability to sequentially load a bunch of images
(PNG only at the time of this writing) and send completion
notifications to its master controller. The class ImageLoader is the
proxy that listens to the notifications generated by the ImagePool
and resends them to the SkinBuilder class.
Once all the images have been loaded the SkinBuilder
reacts to the notification "ImagesLoaded()" and starts
synchronously building the needed skins.For now ImagePool loads
images and stores them into a vector. Addressing each individual
image can be done by the vector index only. This will change in the
near future once we transfer all the functionality in this file into
an XML based skin configuration file. At that point various skins and
images used by it will have customizable string names.
The creation of each skin consists of 2 steps:
associating a state to a particular background
image "AddStateSkin()"Unless you know really well what you
are doing, the default state (identified by both mask and value
zero) should always be defined so that the view can always draw a
background. There is one view where we commonly do not define any
skin states: the label. For labels we simply do not want a
background drawn, and rely on the background of the container. You
may choose to think differently :-)
agregating any number of skin definitions as
components of a larges skin "AddPart()". If the skin is to
be used on a view that does not have agregated children then there
is no need to define any agregate skins. Each agregated skin part
has a part-name. The part name is used by the view to identify
which skin part goes to which child.
Take a look at "CreateTabSkin()" as a demo.
For the tabs the states hovered, selected and
default are defined. The images we draw for these states will make
the tab look similar to a raised button.
The tab skin is agregated with the name "tab"
into the bar skin. For the bar itself only the default state is
defined (an inanimate background)
The bar skin is agregated with the name "bar"
into the tab skin. for the entire tab page we did not define a
background and rely on the content of the page to draw something or
for the parent container of the tab to do it. It is frequently a
good ideea to just omit assigning a background to containers (as
tab page) because this will avoid drawing one background (the child)
on top of another (the parent) and waste precious drawing time.
Notice that for each state skin we assign an
image but also add a rectangle. The purpose of the rectangle is to
define a "content" region to the view. This allows the
skin designer to define where the content goes instead of requiring
the programer to hard code such things. Of course as of now, no XML
skin configuration is available so the feature is not plainly
helpfull.During the drawing of the skin the content area gets
stretched to cover most of the view's bounding rectangle. The edges
outside of the content area will have fixed width / height. This is
all you need to draw fancy looking borders.
Once the process of building the skins is complete
the SkinBuilder broadcasts the appropriate notification
(SkinBuildComplete or SkinBuildFailed) to the listening application.