Screwtopia - Metacity theming tips and tricks [entries|archive|friends|userinfo]
Screwtape

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Metacity theming tips and tricks [Feb. 6th, 2009|09:51 pm]
Previous Entry Share Next Entry

I've encountered a number of challenges in my limited Metacity theming experience; in the interest educating Google, I'll explain some of the stumbling blocks I've encountered so-far with my Mac OS Classic theme, and how I worked around them (or not):

Problems and solutions:

  • The rounded_*_* attributes of the <frame_geometry/> element behave strangely in version-2 themes - there's an effective minimum radius of 5 for the bottom two corners, for no particularly good reason. The Mac OS Classic theme has a 1px drop-shadow, so the very top-right and bottom-left pixels are transparent. I tried to emulate this with a 1px radius, but it doesn't work at the bottom-left.

    I filed bug 569654 and gave up.

  • The *_*_background values of the function attribute of the <button/> element don't work. Since all the titlebar buttons in my theme have the same background, I thought I'd save time by setting up all the various backgrounds identically in some base <frame_style/> and inherit from it, but no such luck: the background for a particular button is drawn where that button appears by default rather than where it actually is, making the feature pretty much useless. - this is bug 401726.

    Instead of defining a <draw_ops/> for your button backgrounds and setting it up with the *_*_background values, just use <include/> to include it in each of your button-drawing <draw_ops/> elements.

  • In Mac OS Classic, title-text is centred between the left and right edges of the window, while Metacity draws the title frame piece between the left and right titlebar buttons. Thus, if one side of the titlebar has more than the other, Metacity's title will be subtly off-centre. Which method is better largely depends on how unbalanced your button layout is - Metacity's default has three times as many buttons on one side than the other, so I think they made the right choice.

    It would be nice if there were some way to work around this, but I couldn't find one, so I'm putting up with this minor inexactitude.

  • Mac OS Classic utility window titlebars are drawn with a particular 2x2 stipple pattern which is easily replicated with <tile>. However, the smaller the source-image for your tiling, the slower your windows will draw. I enormously sped up drawing the stripes on normal titlebars by switching from a 2x2 tile to a 100x2 tile. I could do the same for utility windows if I could quickly draw a 100px-long line with the required pattern, but the obvious solution of:

        <line x1="0" y1="0" x2="100" y2="0"
            dash_on_length="1" dash_off_length="1"/>

    ...had no observable effect.

    I wound up going with the 2x2 tile. Utility windows are rare, and metacity-theme-viewer doesn't measure their performance, so hopefully it's not all that bad.

  • Mac OS Classic draws every normal window with a 1px drop-shadow, so a shaded window is drawn as a regular titlebar plus an extra 1px at the bottom for the shadow. Metacity draws a shaded window entirely in the space the <frame_geometry/> sets aside for the titlebar, so my shaded windows were appearing unnaturally cramped along the bottom edge. (Of the standard themes, Bright and Crux both suffer similarly)

    My solution to that was to introduce a custom <frame_geometry/> just for shaded windows that increased the titlebar size by 1px and increased the button-border by 1px to compensate. Unfortunately, this introduced a second problem: Whenever the <frame_geometry/> that applies to a window changes, Metacity moves the window decorations around so that the actual window content stays in exactly the same place - so adding 1px to the titlebar height in shaded mode means the entire titlebar gets shifted up a pixel in shaded mode.

    I wound up having to put up with this, too.

  • Utility windows' titles in Mac OS Classic are drawn with a different font from normal windows' titles, but often they don't have title-text at all. Since I can't configure Metacity to use a different font for different window types, I went for the second-best alternative of not drawing titles at all.

    However, in order to get the size of utility windows' titlebars correct,I had to set the title_scale attribute on the <frame_geometry/> element to xx-small and add padding until the titlebar was the correct height. Thus, my theme now has an obscure dependency on theexact definition of xx-small and probably the exact metrics of my current titlebar font.

    Once again, I'm putting up with this for lack of alternatives. However, since I'm emulating a specific OS revision's appearance, I might get away with using a specific bitmap font that's designed to have the correct metrics at various scales.

  • Mac OS Classic titlebars have 6px of whitespace padding on either side of the title-text. In the basic case this is very easy to draw, however it gets more complicated when the title-text is too long to fit in the available space. For example, if a window has 100px of space available for a title, and title-text that's 150px wide, I would like to show 6px of whitespace, 88px of title-text and then another 6px of whitespace. It seems that <include/> and <clip/> are powerless to actually clip title-text, so I wound up with 6px of whitespace and 94px of title-text.

    My workaround for this was hackish but effective: after drawing the whitespace rectangle and the title-text, draw another rectangle over the last 6px of whitespace, blotting out any text that might have spilled over.

  • There's a second problem with the whitespace in Mac OS titlebars: when a window has no title text at all, there should be no whitespace at all - just un-interrupted stripes. The simple expression I started with wound up drawing a 12px blank patch in the middle of the titlebar, if there was no text.

    If there were some kind of if/then statement in the Metacity expression language, I could just have said if title_width == 0 then padding_width=0 or similar, but there isn't. After some thought, I realised what I wanted was a function that evaluated to zero if title_width was less than 1, but evaluated to title_width + 12 if title_width were ≥ 1:

    (title_width * 14) `min` (title_width + 12)

    If title_width is zero, the left-hand side is zero and hence the result of the `min` operator is zero. Otherwise, the left-hand side will be greater than the right-hand side, and we'll get the width we want.This seemed brilliant up until I tried it out and discovered that when a <rectangle/> is given a width of 0, it's actually drawn with a width of 1, and I wound up with two vertical stripes in my title-bar (one for the main padding rectangle, and one for the 'touch up text overflow' rectangle.

    That disappointed me, but I got to thinking again. Now that I'd cracked emulating boolean logic in an expression, I didn't have to set the width attribute just because I was testing title_width; I could change just about anything. Eventually I decided the safest thing to do would be shift the padding-rectangles off the top of the window-frame if there was no title-text (shifting downwards might have interfered with the bottom border on short windows) and I wound up with the following command to draw the padding rectangle:

    <rectangle color="white" filled="true"
    	x="0 `max` (width - (title_width+12))/2" 
    	y="((title_width*height) `min` ((height-title_height)/2+height))-height"
    	width="width `min` (title_width+12)" 
    	height="title_height"/>

    Because I wanted the expression to evaluate to -height rather than 0 if title_width was 0, I had to put -height at the end of the expression and hence needed a +height in the right-hand side to balance it out. And after all that, it works beautifully. :)

Workflow tips:

  • If you want to make a theme that requires theme-version-2 features, you'll need to put an empty version-1 theme file beside the version-2 theme file, otherwise the GNOME Appearance Preferences dialog will ignore your theme.
  • You probably do want to make a version-2 theme - in most cases, the ability to define constants for colours is worth having to add a few empty stanzas for buttons nobody uses.
  • Try metacity-theme-viewer first. It actually prints helpful error messages if your theme has a problem, and it gives you a reasonable overview of what it will look like. Sadly, it does not handle corner rounding, it draws titlebars with GNOME's "Application font", not the "Window title font", and it doesn't emulate any of the interactivity of a theme such as button prelight/pressed states or unfocussed windows.
  • Next, you'll have to set Metacity to use your theme-in-development and run metacity-message reload-theme so you can test out the basic interactive things with windows on your desktop.
  • In GNOME's Window Preferences dialog, you should set "Titlebar Action" to "Roll up" rather than the default of "Minimize" - there's no other easy way to test the "shaded" window state unless you add the 'shade' button to the titlebar, which is only available for version 2 themes.
  • In order to test all the buttons, you should play with the /apps/metacity/general/button_layout GConf key - most of the available buttons aren't shown by default, and the only way to test their prelight/pressed states is interactively. I'm not sure if this is documented anywhere, but the complete set of button names is: menu, shade, above, stick, minimize, maximize, and close
  • For covering all the theming corner cases, run metacity-window-demo and open some of its sample windows. Not all the different kinds of windows are themed differently, but if you need to try a Utility window or a Modal Dialog in a hurry, it's the best place to go.

The result (so far):

LinkReply

Comments:
[User Picture]From: virtualwolf
2009-02-06 11:22 pm (UTC)

(Link)

Hahaha! That's fantastic. :D
[User Picture]From: nornagon
2009-02-07 09:03 pm (UTC)

(Link)

Upload pls