Up
Authors
- Adam Fedor (
fedor@gnu.org
)
-
- Richard Frith-Macdonald (
rfm@gnu.org
)
-
Useful/configurable drawing functions
Copyright: (C) 2004-2006 Free Software Foundation, Inc.
The theme management system for the GNUstep GUI is based
around the
GSTheme
class, which provides support for loading of theme
bundles and methods for drawing common user interface
elements.
The theme system works in
conjunction with a variety of other GUI classes
and is intended to eventually allow for very major
changes in GUI appearance and behavior.
Various design imperatives apply to the theme system,
but probably the key ones are:
-
It should allow designers and other non-technical users
to easily develop new and interesting GUI styles likely
to attract new users to GNUstep.
-
Using and switching between themes should be an easy
and pleasant experience... so that people are not put
off when they try using themes.
-
It should eventually permit a GNUstep application to
appear as a native application on ms-windows and
other systems.
To attain these aims implies the recognition of some more
specific objectives and some possible technical
solutions:
-
We must have as simple as possible an API for the
functions handling the way GUI elements work and
the way they draw themselves.
The standard
OpenStep/MacOS-X API provides mechanisms
for controlling the colors used to draw controls (via
NSColor
and
NSColorList
) and controlling the way controls behave
(NSInterfaceStyleForKey() and
[NSResponder -interfaceStyle]
), but we need to extend that with methods to draw controls entirely differently if required.
-
We must have a GUI application for theme
development. It is not sufficient to
provide an API if we want good graphic
designers and user interface specialists to
develop themes for us.
-
It must be possible for an application to
dynamically change the theme in use while
it is running and it should be easy for a user to
select between available themes.
This
implies that themes must be loadable bundles
and that it must always be possible to unload a
theme as well as loading one.
It suggests
that the theme selection mechanism should be in
every application, perhaps as an extension to an
existing panel such as the info panel.
There are various aspects of theming which can be
treated pretty much separately, so there is no
reason why a theme might not be created which
just employs one of these mechanisms.
- System images
-
Possibly the simples theme change... a theme
might supply a new set of system images used
for arrows and other icons that the GUI decorates
controls with.
- System colors
-
A theme might simply define a new system color
list, so that controls are drawn in a new color
range, though they would still function the
same way. Even specifying new colors can make
the GUI look quite different though.
Beyond system colors, the theming API also
provides a mechanism for specifying colors
for particular parts of GUI controls.
- Image tiling
-
Controls might be given sets of images used
as tiling to draw themselves rather than using the
standard line drawing and color fill
mechanisms.
- Interface style
-
A theme might supply a set of interface style keys
for various controls, defining how those controls
should behave subject to the limitation of the
range of behaviors coded into the GUI library.
- Method override
-
A theme might actually provide code, in the form of
a subclass of
GSTheme
such that drawing methods have completely
custom behavior.
While many themes can be created without
subclassing GSTheme, there are some cases
where writing code is necessary (most notably
when interfacing to a native theming engine for
some platform in order to make a GNUstep app
have a native look).
In these cases the
subclass should follow certain rules in
order to operate cleanly and efficiently:
- Stability
-
Theme operation should remain consistent
while it is in use, particularly in the
provision of resources such as images and
colors.
If a theme needs to change a
resource (such as an image) then it should
do so by calling
-deactivate
, making the change, and then calling
-activate
. This sequence ensures that the GUI library is
made aware of any changes and can redraw the
screen.
The deactivation/activation
sequence is expensive, so the subclass
should attempt to combine multiple resource
updates into a group rather than performing
the deactivation/activation for each resource
individually.
- Activation
-
The standard
-activate
method replaces existing system images,
colors, interface style settings and other
user defaults settings with versions stored in
the theme bundle.
If a subclass wishes
to dynamically provide these resources rather
than supplying them as static information in
the bundle, it may update the in-memory
information after the normal operation
has taken place. This should be done by the
theme registering itsself as an observer of
GSThemeWillActivateNotification
and adding the resources just before the theme
becomes active.
Cleanup may be done
in response to a
GSThemeWillDeactivateNotification
(called before the default cleanup) or a
GSThemeDidDeactivateNotification
(called after the default cleanup).
- Versioning
-
With a theme which contains only static
resources, versioning is not much of an
issue, but with a code-based theme (ie where
you subclass GSTheme) versioning does become
very important. This is because, while you can
load code from a bundle, you can't unload it
again... so if you have two versions of a
theme where the subclass has the same name,
then you have a conflict and can't load both
and swap between the two.
Thematic.app
solves this problem my incorporating a
version number into the name of the GSTheme
subclasses it creates, but that's not
the only consideration...
You must also
ensure that either you do not define any
other classes in your theme or that, if you
do define them you make sure that each of them
incorporates the theme version number.
A similar consideration would apply
to any categories, however category conflicts
are far more difficult to resolve since even
with different version names of the
categories, the categories all effect
the same class/methods. In fact this issue is
so tricky to deal with that you should simply
not use categories within your theme code.
To work around this limitation, the
GSTheme class supports overriding of the
methods of any other class while the theme
is active. See the
-overriddenMethod:for:
method for more information.
- Image override
-
System images (those returned by the
[NSImage +imageNamed:]
method) are handled by the default theming mechanism, for each system image supplied in the theme bundle, the image is loaded from the bundle and used while the theme is active. Any pre-existing in-memory image is saved on theme activation and restored on theme deactivation.
A theme subclass may override the -imageClass
method to change the class used to load each image from the bundle... thus allowing customisation of not just the images but also of the image behavior in the (very rare) cases where this is desirable.
Finally, a theme may provide application specific images which are loaded in preference to named images from the application's own bundle. These images are simply stored in a subdirectory whose name is the same as the application's bundleIdentifier.
Up