windows-nt/Source/XPSP1/NT/shell/themes/themedir/controls.txt
2020-09-26 16:20:57 +08:00

160 lines
6.9 KiB
Plaintext

Theme Aware Controls - 08/03/00
-------------------------------
HOW TO MAKE CONTROLS/WINDOWS THEME AWARE
Here are the high-level steps needed to make a control theme-aware:
Preparation
a. The control author decides which aspects of the control will be
theme aware.
b. He then divides the control up into 1 or more
named theme-aware child parts (drawn shapes with optional text).
c. For each part, the author can define 1 or more background images
in a single bitmap file. These difference backgrounds are usually
associated with different states in the control but don't have to
be. At run time, the appropriate image can be selected using an
"iStateId" value as the 1-based index to the correct image.
d. alternatively, the author can decide the create the background based
on a border color/style/size and a fill color/style.
e. other ways of rendering may be defined in the future; try to keep
your control as isolated as possible from the particular theme properties
and try to use the theme drawing API's exclusively.
f. the control author then publishes the theme schema for his control. If the control is
part of comctrls v6, then the schema is added to the file "TmSchema.h"; otherwise,
the control creates his own "xxxSchema.h" file (which needs to get compiled into
his control as well as registered with the theme manager).
g. add schema info:
- he adds a parts enum to the schema file; ex:
BEGIN_TM_CLASS_PARTS(EDIT)
TM_PART(EP, EDITTEXT)
TM_PART(EP, CARET)
END_TM_CLASS_PARTS()
- for each part that has more than 1 state, he adds a state enum:
BEGIN_TM_PART_STATES(EDITTEXT)
TM_STATE(ETS, NORMAL)
TM_STATE(ETS, HOT)
TM_STATE(ETS, SELECTED)
TM_STATE(ETS, DISABLED)
TM_STATE(ETS, FOCUSED)
TM_STATE(ETS, READONLY)
TM_STATE(ETS, ASSIST)
END_TM_PART_STATES()
h. note that once "TmSchema.h" has been edited, the "\nt\shell\published\inc" directory
must be built so that the "TmSchema.h" file is copied to "\nt\public\sdk\inc".
Code Changes
a. obtain an HTHEME handle to call thememgr drawing routines with. this should
be done during control creation (as soon as the "hwnd" is available).
The code for the button control would look something like this:
HTHEME hTheme;
hTheme = OpenThemeData(hwnd, L"button");
if (! hTheme) // fall back on old drawing code...
{
}
b. when its time to paint one of the parts of the control:
i. first initialize the control's DC; most controls send a WM_CTLCOLORXXX
msg to their parent to do this. Use the HBRUSH returned from the WM_CTLCOLORXXX
msg as the DefaultBrush passed to the theme drawing routines.
ii. pass "hTheme" and the DefaultBrush to the theme drawing routines to paint
the background, text, line, border, etc. The routines will ensure that the
painting is done in a theme-compliant way.
The code for drawing the a push button background would look something like:
//---- initialize the DC & get DefaultBrush ----
HBRUSH hDefaultBrush = (HBRUSH)SendMessage(GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hwnd);
if (hTheme)
{
int iStateId;
if (fPushed)
iStateId = 4;
else if (fDisabled)
iStateId = 3;
else if (fMouseOver)
iStateId = 2;
else if (fDefault)
iStateId = 1;
else
iStateId = 0;
//---- DrawThemeBackground doesn't yet accept the hDefaultBrush param ----
hr = DrawThemeBackgound(hTheme, hdc, TMT_BUTTON, iStateId, &clientRect, 0);
}
else // old drawing code
{
}
c. when a WM_THEMECHANGED msg is received by the control/window, it must close its
current HTMEME handle, try to obtain a new handle, and repaint its control.
The code would look something like:
CloseThemeData(hTheme);
hTheme = OpenThemeData(hwnd, L"BUTTON");
InvalidateRect(hwnd, NULL, TRUE);
d. The ThemeRender object should be able to handle most or all of the
drawing for the control. However, when the control needs to access
some theme information directly, the programmer can pass the "hTheme"
handle to one of the theme info "getter" routines. For example, the
code to get the FONT for a part would look something like:
if (hTheme)
{
LOGFONT *pFont;
hr = GetThemeFont(hTheme, TMBU_BUTTON, 0, pvFONT, &pFont);
if (SUCCEEDED(hr))
{
<use the pFont here...>
}
}
e. Since its possible that the control's main background may contain
transparent parts, the programmer will need to handle the WM_NCHITTEST msg
processing. The code would look something like this:
if ((msg == WM_NCHITTEST) && (hTheme))
{
int val = DefWindowProc(msg, hwnd, wparam, lparam);
if (val == HTCLIENT) // test further
{
BOOL fHit;
hr = HitTestThemeBackground(hTheme, hdc, TMBU_BUTTON, 0, iStateIndex,
&clientRect, &fHit);
if ((SUCCEEDED(hr)) && (! fHit))
val = HTTRANSPARENT;
}
return val;
}
f. The programmer needs to be aware that being themed can change the size of
various parts. For example, a background that used to have a 2-pixel border
around it may now use a 6-pixel border. The programmer should use methods
like "GetThemeBackgroundContentRect()" to determine which parts of the background
content can be put into and "GetThemeTextExtent()" to find out how much space
the text needs in its theme-selected font.