Yesterday talks were wicked interesting, especially the Alexis talk about the new animation framework was really breathtaking.
Aaanyways, this is kinda a synopsis of what i talked about in my part during
the talk session yesterday at Tokamak, slides
here,
it’s quickly written in few minutes, so the english it’s a total horror,
but i feel it could be kinda interesting anyways 🙂
Themes
Pretty much everything you see in Plasma is based on SVG graphics,
this means that you have a really high degree of customizability
controlled by the Plasma theme. This also means that compared to
classical Qt themes the entry barrier is significantly lower, because
of course you have to be a good designer to make one, but not a
programmer, while to do a Qt theme you have to be both and this is a
really really rare thing.
Also, since themes are pure graphics without binary code they are
completely platform independent so they can be distributed trough the
GHNS framework.
Being vector graphics, we can display them without problem from
very little screen to huge monsters when we will have 600dpi screens
that won’t be a problem, specially for the theme elements based on
the FrameSvg concept.
From a programmer point of view using the theme graphics is quite
easy too, because the two main classes that manages Svg themes (Svg
and FrameSvg) are very abstracted, so you won’t have to bother that
the graphics is actually a Svg.
Plasma themes will be installed under your KDE installation prefix
under share/apps/desktopthemes. The filesystem structure inside the
theme has got two main subfolders: widgets, meant for element
drawn on canvas and dialogs, meant for elements that are
actually a top level window. There are two particular and optional
subfolders: the first is locolor, that will have the same two
widgets and dialogs subfolders meant to be used when the theme is
displayed on screens with less than 16 bit color depth.
The other folder is called opaque and has replacements for
the elements that are a top level widget when the desktop effects are
turned off, that’s because in this case we won’t be able to have a
semitransparent window, so we won’t be able to have luxuries like
antialiased borders or drop shadows, if you have a rounded border
there you should use the good old pixelart tecnique from the 80’s.
In order to load an Svg from the current theme is sufficient to
use the setImagePath() function of Plasma::Svg, where you don’t have
to worry neither of the path of the theme or the svg or svgz
extension, so something like “widgets/background” will suffice.
If you are writing a scripted applet you can also include the
graphics alongside the applet code and distribute everything in a
single package, from a javascript applet for instance it will be
sufficient to call the function plasmoid.findSvg(“foo”) and the
proper foo.svg file will be located for you.
That said however now i have to get a bit annoying with some
advices: you should be really really careful when you add custom
graphics in your applet, both when you install it alongside your c++
applet and also when you embed it in the package of your scripted
applet, because your additional graphics must work with as much
themes as possible, and this is an hell lot difficult. You should at
least try it with both a dark and a light theme, but in the end… if
you want to do that, think again 🙂
Boring implementation details
Usually in your applet won’t have to directly paint Svgs, because
if you just use the default plasma widgets you’ll have the svg
painting done for you, but if you have to draw some svg elements you
have to know two classes: Plasma::Svg and Plasma::FrameSvg.
The mighty machine that manages all the svg painting in Plasma is
the Plasma::svg class, that internally uses the QSvgRenderer class,
but optimizes it as much as possible: in the context of the whole
Plasma application for each svg file we get an unique shared svg
renderer and the rendering result is saved to disk thanks to a
KPixmapCache, that can avoid the creation of renderer to a satisfatry
degree: in the second plasma start won’t be created a single renderer
until you resize something, saving a bit of startup time and some
megs of ram.
The most important functions you are interested if you want to use
a Svg are its several paint functions (overloaded with QPoints or
QRects as parameters) the already mentioned setImagePath() resize()
and various functions to access the svg sub elements like
hasElement(), elementSize() and elementRect().
The other class used in plasma to render Svgs works at a slightly
higher level of abstraction and is Plasma::FrameSvg. Usually widgets
are mostly rectangular things, but even if Svg is scalable that
doesn’t mean the result will look pretty, take a look at the slides
example for instance, where a
default applet background is heavily vertically stretched, so the
horizontal borders become thicker that the vertical ones, and to make
things worse they usually aren’t even an integer number of pixels
making the result to look really blurred.
So, what is there in the default applet background? We can see
there are actually 9 pieces: the corners, the edges and the central
part (called with a great stretch of fantasy center, top, topleft,
topright, left,right,bottom, bottomright and bottomleft). When the
thing will get painted not all elements will be scaled, it’s
important that the corners won’t be never ever scaled, while the
horizontal edges will be scaled only horizontally and similarly the
vertical edges will be scaled only vertically, while the center
element will scale freely.
From a boring code standpoint FrameSvg inherit the Svg class, so
all the stuff that is available in Svg is available in FrameSvg too,
but with the difference that you want to resize the image with
resizeFrame() that uses the method i talked before and you’ll paint
the correctly resize Svg with paintFrame(), while paint() is still
useful to paint single elements in the Svg. Also a single Svg can
contain multiple series of 9 elements, with the names differentiated
by a proper prefix, that can be chosen with the setElementPrefix()
function.