Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
7.1 KiB

  1. Theme Aware Controls - 08/03/00
  2. -------------------------------
  3. HOW TO MAKE CONTROLS/WINDOWS THEME AWARE
  4. Here are the high-level steps needed to make a control theme-aware:
  5. Preparation
  6. a. The control author decides which aspects of the control will be
  7. theme aware.
  8. b. He then divides the control up into 1 or more
  9. named theme-aware child parts (drawn shapes with optional text).
  10. c. For each part, the author can define 1 or more background images
  11. in a single bitmap file. These difference backgrounds are usually
  12. associated with different states in the control but don't have to
  13. be. At run time, the appropriate image can be selected using an
  14. "iStateId" value as the 1-based index to the correct image.
  15. d. alternatively, the author can decide the create the background based
  16. on a border color/style/size and a fill color/style.
  17. e. other ways of rendering may be defined in the future; try to keep
  18. your control as isolated as possible from the particular theme properties
  19. and try to use the theme drawing API's exclusively.
  20. f. the control author then publishes the theme schema for his control. If the control is
  21. part of comctrls v6, then the schema is added to the file "TmSchema.h"; otherwise,
  22. the control creates his own "xxxSchema.h" file (which needs to get compiled into
  23. his control as well as registered with the theme manager).
  24. g. add schema info:
  25. - he adds a parts enum to the schema file; ex:
  26. BEGIN_TM_CLASS_PARTS(EDIT)
  27. TM_PART(EP, EDITTEXT)
  28. TM_PART(EP, CARET)
  29. END_TM_CLASS_PARTS()
  30. - for each part that has more than 1 state, he adds a state enum:
  31. BEGIN_TM_PART_STATES(EDITTEXT)
  32. TM_STATE(ETS, NORMAL)
  33. TM_STATE(ETS, HOT)
  34. TM_STATE(ETS, SELECTED)
  35. TM_STATE(ETS, DISABLED)
  36. TM_STATE(ETS, FOCUSED)
  37. TM_STATE(ETS, READONLY)
  38. TM_STATE(ETS, ASSIST)
  39. END_TM_PART_STATES()
  40. h. note that once "TmSchema.h" has been edited, the "\nt\shell\published\inc" directory
  41. must be built so that the "TmSchema.h" file is copied to "\nt\public\sdk\inc".
  42. Code Changes
  43. a. obtain an HTHEME handle to call thememgr drawing routines with. this should
  44. be done during control creation (as soon as the "hwnd" is available).
  45. The code for the button control would look something like this:
  46. HTHEME hTheme;
  47. hTheme = OpenThemeData(hwnd, L"button");
  48. if (! hTheme) // fall back on old drawing code...
  49. {
  50. }
  51. b. when its time to paint one of the parts of the control:
  52. i. first initialize the control's DC; most controls send a WM_CTLCOLORXXX
  53. msg to their parent to do this. Use the HBRUSH returned from the WM_CTLCOLORXXX
  54. msg as the DefaultBrush passed to the theme drawing routines.
  55. ii. pass "hTheme" and the DefaultBrush to the theme drawing routines to paint
  56. the background, text, line, border, etc. The routines will ensure that the
  57. painting is done in a theme-compliant way.
  58. The code for drawing the a push button background would look something like:
  59. //---- initialize the DC & get DefaultBrush ----
  60. HBRUSH hDefaultBrush = (HBRUSH)SendMessage(GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hwnd);
  61. if (hTheme)
  62. {
  63. int iStateId;
  64. if (fPushed)
  65. iStateId = 4;
  66. else if (fDisabled)
  67. iStateId = 3;
  68. else if (fMouseOver)
  69. iStateId = 2;
  70. else if (fDefault)
  71. iStateId = 1;
  72. else
  73. iStateId = 0;
  74. //---- DrawThemeBackground doesn't yet accept the hDefaultBrush param ----
  75. hr = DrawThemeBackgound(hTheme, hdc, TMT_BUTTON, iStateId, &clientRect, 0);
  76. }
  77. else // old drawing code
  78. {
  79. }
  80. c. when a WM_THEMECHANGED msg is received by the control/window, it must close its
  81. current HTMEME handle, try to obtain a new handle, and repaint its control.
  82. The code would look something like:
  83. CloseThemeData(hTheme);
  84. hTheme = OpenThemeData(hwnd, L"BUTTON");
  85. InvalidateRect(hwnd, NULL, TRUE);
  86. d. The ThemeRender object should be able to handle most or all of the
  87. drawing for the control. However, when the control needs to access
  88. some theme information directly, the programmer can pass the "hTheme"
  89. handle to one of the theme info "getter" routines. For example, the
  90. code to get the FONT for a part would look something like:
  91. if (hTheme)
  92. {
  93. LOGFONT *pFont;
  94. hr = GetThemeFont(hTheme, TMBU_BUTTON, 0, pvFONT, &pFont);
  95. if (SUCCEEDED(hr))
  96. {
  97. <use the pFont here...>
  98. }
  99. }
  100. e. Since its possible that the control's main background may contain
  101. transparent parts, the programmer will need to handle the WM_NCHITTEST msg
  102. processing. The code would look something like this:
  103. if ((msg == WM_NCHITTEST) && (hTheme))
  104. {
  105. int val = DefWindowProc(msg, hwnd, wparam, lparam);
  106. if (val == HTCLIENT) // test further
  107. {
  108. BOOL fHit;
  109. hr = HitTestThemeBackground(hTheme, hdc, TMBU_BUTTON, 0, iStateIndex,
  110. &clientRect, &fHit);
  111. if ((SUCCEEDED(hr)) && (! fHit))
  112. val = HTTRANSPARENT;
  113. }
  114. return val;
  115. }
  116. f. The programmer needs to be aware that being themed can change the size of
  117. various parts. For example, a background that used to have a 2-pixel border
  118. around it may now use a 6-pixel border. The programmer should use methods
  119. like "GetThemeBackgroundContentRect()" to determine which parts of the background
  120. content can be put into and "GetThemeTextExtent()" to find out how much space
  121. the text needs in its theme-selected font.