[uxtheme]
authorGiannis Adamopoulos <gadamopoulos@reactos.org>
Fri, 17 Jan 2014 17:52:58 +0000 (17:52 +0000)
committerGiannis Adamopoulos <gadamopoulos@reactos.org>
Fri, 17 Jan 2014 17:52:58 +0000 (17:52 +0000)
- Greatly reduce needless repaints in the non client area but remembering the last hittest of the mouse events to redraw it only when it is needed
- Do not reset the theme region every time we move a window, also make sure we try to set a theme region only when it is needed
- Do not use OpenThemeData but use MSSTYLES_OpenThemeClass directly
- Do not reload the active theme every time we get a WM_THEMECHANGED message, make sure to see if the active theme was already loaded before trying to load it again
- See CORE-7775

svn path=/trunk/; revision=61654

reactos/dll/win32/uxtheme/nonclient.c
reactos/dll/win32/uxtheme/system.c
reactos/dll/win32/uxtheme/themehooks.c
reactos/dll/win32/uxtheme/uxthemep.h

index ad9365f..99fd38d 100644 (file)
@@ -155,9 +155,8 @@ ThemeInitDrawContext(PDRAW_CONTEXT pcontext,
     GetWindowInfo(hWnd, &pcontext->wi);
     pcontext->hWnd = hWnd;
     pcontext->Active = IsWindowActive(hWnd, pcontext->wi.dwExStyle);
-    pcontext->hPrevTheme = GetPropW(hWnd, (LPCWSTR)MAKEINTATOM(atWindowTheme));
-    pcontext->theme = OpenThemeData(pcontext->hWnd,  L"WINDOW");
-    pcontext->scrolltheme = OpenThemeData(pcontext->hWnd,  L"SCROLLBAR");
+    pcontext->theme = MSSTYLES_OpenThemeClass(ActiveThemeFile, NULL, L"WINDOW");
+    pcontext->scrolltheme = MSSTYLES_OpenThemeClass(ActiveThemeFile, NULL, L"SCROLLBAR");
 
     pcontext->CaptionHeight = pcontext->wi.cyWindowBorders;
     pcontext->CaptionHeight += GetSystemMetrics(pcontext->wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMCAPTION : SM_CYCAPTION );
@@ -179,8 +178,6 @@ ThemeCleanupDrawContext(PDRAW_CONTEXT pcontext)
     CloseThemeData (pcontext->theme);
     CloseThemeData (pcontext->scrolltheme);
 
-    SetPropW(pcontext->hWnd, (LPCWSTR)MAKEINTATOM(atWindowTheme), pcontext->hPrevTheme);
-
     if(pcontext->hRgn != NULL)
     {
         DeleteObject(pcontext->hRgn);
@@ -289,10 +286,7 @@ ThemeDrawCaptionButtons(PDRAW_CONTEXT pcontext, DWORD htHot, DWORD htDown)
 {
     RECT rcCurrent;
 
-    /* Check if the window has caption buttons */
-    if (!((pcontext->wi.dwStyle & WS_CAPTION) && (pcontext->wi.dwStyle & WS_SYSMENU)))
-        return ;
-
+    /* Calculate the area of the caption */
     rcCurrent.top = rcCurrent.left = 0;
     rcCurrent.right = pcontext->wi.rcWindow.right - pcontext->wi.rcWindow.left;
     rcCurrent.bottom = pcontext->CaptionHeight;
@@ -336,7 +330,7 @@ ThemeDrawCaption(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
 
     /* Draw the caption background*/
     rcPart = *prcCurrent;
-    rcPart.bottom = pcontext->CaptionHeight;
+    rcPart.bottom = rcPart.top + pcontext->CaptionHeight;
     prcCurrent->top = rcPart.bottom;
     DrawThemeBackground(pcontext->theme, pcontext->hDC,iPart,iState,&rcPart,NULL);
 
@@ -400,7 +394,7 @@ ThemeDrawBorders(PDRAW_CONTEXT pcontext, RECT* prcCurrent)
 
     /* Draw the left border */
     rcPart = *prcCurrent;
-    rcPart.right = pcontext->wi.cxWindowBorders ;
+    rcPart.right = rcPart.left + pcontext->wi.cxWindowBorders ;
     prcCurrent->left = rcPart.right;
     DrawThemeBackground(pcontext->theme, pcontext->hDC,WP_FRAMELEFT, iState, &rcPart, NULL);
 
@@ -684,29 +678,57 @@ ThemeHandleNcMouseMove(HWND hWnd, DWORD ht, POINT* pt)
 {
     DRAW_CONTEXT context;
     TRACKMOUSEEVENT tme;
+    DWORD style;
+    PWND_CONTEXT pcontext;
 
+    /* First of all check if we have something to do here */
+    style = GetWindowLongW(hWnd, GWL_STYLE);
+    if((style & (WS_CAPTION|WS_HSCROLL|WS_VSCROLL))==0)
+        return 0;
+
+    /* Get theme data for this window */
+    pcontext = ThemeGetWndContext(hWnd);
+    if (pcontext == NULL)
+        return 0;
+
+    /* Begin tracking in the non client area if we are not tracking yet */
     tme.cbSize = sizeof(TRACKMOUSEEVENT);
     tme.dwFlags = TME_QUERY;
     tme.hwndTrack  = hWnd;
     TrackMouseEvent(&tme);
-        if (tme.dwFlags != (TME_LEAVE | TME_NONCLIENT))
+    if (tme.dwFlags != (TME_LEAVE | TME_NONCLIENT))
     {
         tme.hwndTrack  = hWnd;
         tme.dwFlags = TME_LEAVE | TME_NONCLIENT;
         TrackMouseEvent(&tme);
     }
 
-    ThemeInitDrawContext(&context, hWnd, 0);
-    ThemeDrawCaptionButtons(&context, ht, 0);
+    /* Dont do any drawing if the hit test wasn't changed */
+    if (ht == pcontext->lastHitTest)
+        return 0;
 
-   if(context.wi.dwStyle & WS_HSCROLL)
-        ThemeDrawScrollBar(&context, SB_HORZ , ht == HTHSCROLL ? pt : NULL);
+    ThemeInitDrawContext(&context, hWnd, 0);
+    if (context.wi.dwStyle & WS_SYSMENU)
+    {
+        if (HT_ISBUTTON(ht) || HT_ISBUTTON(pcontext->lastHitTest))
+            ThemeDrawCaptionButtons(&context, ht, 0);
+    }
 
-    if(context.wi.dwStyle & WS_VSCROLL)
-        ThemeDrawScrollBar(&context, SB_VERT, ht == HTVSCROLL ? pt : NULL);
+   if (context.wi.dwStyle & WS_HSCROLL)
+   {
+       if (ht == HTHSCROLL || pcontext->lastHitTest == HTHSCROLL)
+           ThemeDrawScrollBar(&context, SB_HORZ , ht == HTHSCROLL ? pt : NULL);
+   }
 
+    if (context.wi.dwStyle & WS_VSCROLL)
+    {
+        if (ht == HTVSCROLL || pcontext->lastHitTest == HTVSCROLL)
+            ThemeDrawScrollBar(&context, SB_VERT, ht == HTVSCROLL ? pt : NULL);
+    }
     ThemeCleanupDrawContext(&context);
 
+    pcontext->lastHitTest = ht;
+
     return 0;
 }
 
@@ -714,18 +736,33 @@ static LRESULT
 ThemeHandleNcMouseLeave(HWND hWnd)
 {
     DRAW_CONTEXT context;
+    DWORD style;
+    PWND_CONTEXT pWndContext;
+
+    /* First of all check if we have something to do here */
+    style = GetWindowLongW(hWnd, GWL_STYLE);
+    if((style & (WS_CAPTION|WS_HSCROLL|WS_VSCROLL))==0)
+        return 0;
+
+    /* Get theme data for this window */
+    pWndContext = ThemeGetWndContext(hWnd);
+    if (pWndContext == NULL)
+        return 0;
 
     ThemeInitDrawContext(&context, hWnd, 0);
-    ThemeDrawCaptionButtons(&context, 0, 0);
+    if (context.wi.dwStyle & WS_SYSMENU && HT_ISBUTTON(pWndContext->lastHitTest))
+        ThemeDrawCaptionButtons(&context, 0, 0);
 
-   if(context.wi.dwStyle & WS_HSCROLL)
-        ThemeDrawScrollBar(&context, SB_HORZ, NULL);
+   if (context.wi.dwStyle & WS_HSCROLL && pWndContext->lastHitTest == HTHSCROLL)
+        ThemeDrawScrollBar(&context, SB_HORZ,  NULL);
 
-    if(context.wi.dwStyle & WS_VSCROLL)
+    if (context.wi.dwStyle & WS_VSCROLL && pWndContext->lastHitTest == HTVSCROLL)
         ThemeDrawScrollBar(&context, SB_VERT, NULL);
 
     ThemeCleanupDrawContext(&context);
 
+    pWndContext->lastHitTest = HTNOWHERE;
+
     return 0;
 }
 
@@ -733,17 +770,19 @@ static VOID
 ThemeHandleButton(HWND hWnd, WPARAM wParam)
 {
     MSG Msg;
-    BOOL Pressed = TRUE; // , OldState;
+    BOOL Pressed = TRUE;
     WPARAM SCMsg, ht;
     ULONG Style;
     DRAW_CONTEXT context;
+    PWND_CONTEXT pWndContext;
 
     Style = GetWindowLongW(hWnd, GWL_STYLE);
+    if (!((Style & WS_CAPTION) && (Style & WS_SYSMENU)))
+        return ;
+
     switch (wParam)
     {
         case HTCLOSE:
-            if (!(Style & WS_SYSMENU))
-                return;
             SCMsg = SC_CLOSE;
             break;
         case HTMINBUTTON:
@@ -760,8 +799,14 @@ ThemeHandleButton(HWND hWnd, WPARAM wParam)
             return;
     }
 
+    /* Get theme data for this window */
+    pWndContext = ThemeGetWndContext(hWnd);
+    if (pWndContext == NULL)
+        return;
+
     ThemeInitDrawContext(&context, hWnd, 0);
     ThemeDrawCaptionButtons(&context, 0,  wParam);
+    pWndContext->lastHitTest = wParam;
 
     SetCapture(hWnd);
 
@@ -776,14 +821,19 @@ ThemeHandleButton(HWND hWnd, WPARAM wParam)
         if (Msg.message != WM_MOUSEMOVE)
             continue;
 
-        //OldState = Pressed;
         ht = SendMessage(hWnd, WM_NCHITTEST, 0, MAKELPARAM(Msg.pt.x, Msg.pt.y));
         Pressed = (ht == wParam);
 
-        ThemeDrawCaptionButtons(&context, 0, Pressed ? wParam: 0);
+        /* Only draw the buttons if the hit test changed */
+        if (ht != pWndContext->lastHitTest &&
+            (HT_ISBUTTON(ht) || HT_ISBUTTON(pWndContext->lastHitTest)))
+        {
+            ThemeDrawCaptionButtons(&context, 0, Pressed ? wParam: 0);
+            pWndContext->lastHitTest = ht;
+        }
     }
 
-    ThemeDrawCaptionButtons(&context, 0, 0);
+    ThemeDrawCaptionButtons(&context, ht, 0);
     ThemeCleanupDrawContext(&context);
 
     ReleaseCapture();
index 2f2799f..a1c6d36 100644 (file)
@@ -50,7 +50,7 @@ static ATOM atSubAppName;
 static ATOM atSubIdList;
 ATOM atWndContrext;
 
-static PTHEME_FILE ActiveThemeFile;
+PTHEME_FILE ActiveThemeFile;
 
 /***********************************************************************/
 
@@ -138,6 +138,39 @@ static HRESULT UXTHEME_SetActiveTheme(PTHEME_FILE tf)
     return S_OK;
 }
 
+static BOOL bIsThemeActive(LPCWSTR pszTheme, LPCWSTR pszColor, LPCWSTR pszSize)
+{
+    if (ActiveThemeFile == NULL)
+        return FALSE;
+
+    if (wcscmp(pszTheme, ActiveThemeFile->szThemeFile) != 0)
+        return FALSE;
+
+    if (!pszColor[0])
+    {
+        if (ActiveThemeFile->pszAvailColors != ActiveThemeFile->pszSelectedColor)
+            return FALSE;
+    }
+    else
+    {
+        if (wcscmp(pszColor, ActiveThemeFile->pszSelectedColor) != 0)
+            return FALSE;
+    }
+
+    if (!pszSize[0])
+    {
+        if (ActiveThemeFile->pszAvailSizes != ActiveThemeFile->pszSelectedSize)
+            return FALSE;
+    }
+    else
+    {
+        if (wcscmp(pszSize, ActiveThemeFile->pszSelectedSize) != 0)
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
 /***********************************************************************
  *      UXTHEME_LoadTheme
  *
@@ -186,7 +219,14 @@ void UXTHEME_LoadTheme(BOOL bLoad)
         bThemeActive = FALSE;
     }
 
-    if(bThemeActive) {
+    if(bThemeActive)
+    {
+        if( bIsThemeActive(szCurrentTheme, szCurrentColor, szCurrentSize) )
+        {
+            TRACE("Tried to load active theme again\n");
+            return;
+        }
+
         /* Make sure the theme requested is actually valid */
         hr = MSSTYLES_OpenThemeFile(szCurrentTheme,
                                     szCurrentColor[0]?szCurrentColor:NULL,
@@ -471,6 +511,8 @@ static HRESULT UXTHEME_ApplyTheme(PTHEME_FILE tf)
     WCHAR tmp[2];
     HRESULT hr;
 
+    TRACE("UXTHEME_ApplyTheme\n");
+
     if (tf && !ActiveThemeFile)
     {
         UXTHEME_BackupSystemMetrics();
@@ -562,7 +604,7 @@ BOOL WINAPI IsThemeActive(void)
     WCHAR tmp[10];
     DWORD buffsize;
 
-    TRACE("\n");
+    TRACE("IsThemeActive\n");
     SetLastError(ERROR_SUCCESS);
 
     if (ActiveThemeFile) 
index 85081dc..8ce2c24 100644 (file)
@@ -83,7 +83,7 @@ static BOOL CALLBACK ThemeCleanupWndContext(HWND hWnd, LPARAM msg)
     return TRUE;
 }
 
-void SetThemeRegion(HWND hWnd, PWND_CONTEXT pcontext)
+void SetThemeRegion(HWND hWnd)
 {
     HTHEME hTheme;
     RECT rcWindow;
@@ -91,24 +91,10 @@ void SetThemeRegion(HWND hWnd, PWND_CONTEXT pcontext)
     int CaptionHeight, iPart;
     WINDOWINFO wi;
 
-    if(!IsAppThemed())
-    {
-        if(pcontext->HasThemeRgn)
-        {
-            pcontext->HasThemeRgn = FALSE;
-            user32ApiHook.SetWindowRgn(hWnd, 0, TRUE);
-        }
-        return;
-    }
+    TRACE("SetThemeRegion %d\n", hWnd);
 
     wi.cbSize = sizeof(wi);
-
     GetWindowInfo(hWnd, &wi);
-            
-    if((wi.dwStyle & WS_CAPTION)!=WS_CAPTION)
-    {
-        return;
-    }
 
     /* Get the caption part id */
     if (wi.dwExStyle & WS_EX_TOOLWINDOW)
@@ -118,8 +104,6 @@ void SetThemeRegion(HWND hWnd, PWND_CONTEXT pcontext)
     else
         iPart = WP_CAPTION;
 
-    pcontext->HasThemeRgn = TRUE;
-
     CaptionHeight = wi.cyWindowBorders;
     CaptionHeight += GetSystemMetrics(wi.dwExStyle & WS_EX_TOOLWINDOW ? SM_CYSMCAPTION : SM_CYCAPTION );
 
@@ -129,10 +113,8 @@ void SetThemeRegion(HWND hWnd, PWND_CONTEXT pcontext)
     rcWindow.top = 0;
     rcWindow.left = 0;
 
-    hTheme = OpenThemeData (hWnd, L"WINDOW");
-
+    hTheme = MSSTYLES_OpenThemeClass(ActiveThemeFile, NULL, L"WINDOW");
     GetThemeBackgroundRegion(hTheme, 0, iPart, FS_ACTIVE, &rcWindow, &hrgn);
-
     CloseThemeData(hTheme);
 
     GetWindowRect(hWnd, &rcWindow);
@@ -149,21 +131,51 @@ void SetThemeRegion(HWND hWnd, PWND_CONTEXT pcontext)
     user32ApiHook.SetWindowRgn(hWnd, hrgn, TRUE);
 }
 
-int OnPostWinPosChanged(HWND hWnd)
+int OnPostWinPosChanged(HWND hWnd, WINDOWPOS* pWinPos)
 {
-    PWND_CONTEXT pcontext = ThemeGetWndContext(hWnd);
+    PWND_CONTEXT pcontext;
+    DWORD style;
+
+    /* We only proceed to change the window shape if it has a caption */
+    style = GetWindowLongW(hWnd, GWL_STYLE);
+    if((style & WS_CAPTION)!=WS_CAPTION)
+        return 0;
 
-    if(pcontext &&
-        pcontext->HasAppDefinedRgn == FALSE && 
-        pcontext->UpdatingRgn == FALSE)
+    /* Get theme data for this window */
+    pcontext = ThemeGetWndContext(hWnd);
+    if (pcontext == NULL)
+        return 0;
+
+    /* Do not change the region of the window if its size wasn't changed */
+    if ((pWinPos->flags & SWP_NOSIZE) != 0 && pcontext->DirtyThemeRegion == FALSE)
+        return 0;
+
+    /* We don't touch the shape of the window if the application sets it on its own */
+    if (pcontext->HasAppDefinedRgn == TRUE)
+        return 0;
+
+    /* Calling SetWindowRgn will call SetWindowPos again so we need to avoid this recursion */
+    if (pcontext->UpdatingRgn == TRUE)
+        return 0;
+
+    if(!IsAppThemed())
     {
-        pcontext->UpdatingRgn = TRUE;
-        SetThemeRegion(hWnd, pcontext);
-        pcontext = ThemeGetWndContext(hWnd);
-        pcontext->UpdatingRgn = FALSE;
+        if(pcontext->HasThemeRgn)
+        {
+            pcontext->HasThemeRgn = FALSE;
+            user32ApiHook.SetWindowRgn(hWnd, 0, TRUE);
+        }
+        return 0;
     }
-    return 0;
-}
+
+    pcontext->DirtyThemeRegion = FALSE;
+    pcontext->HasThemeRgn = TRUE;
+    pcontext->UpdatingRgn = TRUE;
+    SetThemeRegion(hWnd);
+    pcontext->UpdatingRgn = FALSE;
+
+     return 0;
+ }
 
 /**********************************************************************
  *      Hook Functions
@@ -172,7 +184,7 @@ int OnPostWinPosChanged(HWND hWnd)
 static LRESULT CALLBACK
 ThemeDefWindowProcW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 {      
-    if(!IsThemeActive())
+    if(!IsAppThemed())
     {
         return user32ApiHook.DefWindowProcW(hWnd, 
                                             Msg, 
@@ -190,7 +202,7 @@ ThemeDefWindowProcW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 static LRESULT CALLBACK
 ThemeDefWindowProcA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 {
-    if(!IsThemeActive())
+    if(!IsAppThemed())
     {
         return user32ApiHook.DefWindowProcA(hWnd, 
                                             Msg, 
@@ -211,8 +223,15 @@ ThemePreWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, ULONG_PTR
     switch(Msg)
     {
         case WM_THEMECHANGED:
-            UXTHEME_LoadTheme(TRUE);
-            return 0;
+            if (GetAncestor(hWnd, GA_PARENT) == GetDesktopWindow())
+                UXTHEME_LoadTheme(TRUE);
+        case WM_NCCREATE:
+        {
+            PWND_CONTEXT pcontext = ThemeGetWndContext(hWnd);
+            if (pcontext == NULL)
+                return 0;
+            pcontext->DirtyThemeRegion = TRUE;
+        }
     }
 
     return 0;
@@ -226,7 +245,7 @@ ThemePostWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, ULONG_PTR
     {
         case WM_WINDOWPOSCHANGED:
         {
-            return OnPostWinPosChanged(hWnd);
+            return OnPostWinPosChanged(hWnd, (WINDOWPOS*)lParam);
         }
         case WM_DESTROY:
         {
index c54e193..15b3608 100644 (file)
@@ -8,6 +8,7 @@
 #include <windowsx.h>
 #include <undocuser.h>
 #include <uxtheme.h>
+#include <uxundoc.h>
 #include <vfwmsgs.h>
 #include <tmschema.h>
 
@@ -111,14 +112,17 @@ LPCWSTR UXINI_GetNextValue(PUXINI_FILE uf, DWORD *dwNameLen, LPCWSTR *lpValue, D
 BOOL UXINI_FindValue(PUXINI_FILE uf, LPCWSTR lpName, LPCWSTR *lpValue, DWORD *dwValueLen);
 
 
-
+/* The window context stores data for the window needed through the life of the window */
 typedef struct _WND_CONTEXT
 {
+    UINT lastHitTest;
     BOOL HasAppDefinedRgn;
     BOOL HasThemeRgn;
     BOOL UpdatingRgn;
+    BOOL DirtyThemeRegion;
 } WND_CONTEXT, *PWND_CONTEXT;
 
+/* The draw context stores data that are needed by the drawing operations in the non client area of the window */
 typedef struct _DRAW_CONTEXT
 {
     HWND hWnd;
@@ -168,6 +172,8 @@ enum SCROLL_HITTEST
     SCROLL_BOTTOM_ARROW  /* Bottom or right arrow */
 };
 
+#define HT_ISBUTTON(ht) ((ht) == HTMINBUTTON || (ht) == HTMAXBUTTON || (ht) == HTCLOSE || (ht) == HTHELP)
+
 #define HASSIZEGRIP(Style, ExStyle, ParentStyle, WindowRect, ParentClientRect) \
             ((!(Style & WS_CHILD) && (Style & WS_THICKFRAME) && !(Style & WS_MAXIMIZE))  || \
              ((Style & WS_CHILD) && (ParentStyle & WS_THICKFRAME) && !(ParentStyle & WS_MAXIMIZE) && \
@@ -208,6 +214,7 @@ extern HINSTANCE hDllInst;
 extern ATOM atWindowTheme;
 extern ATOM atWndContrext;
 extern BOOL gbThemeHooksActive;
+extern PTHEME_FILE ActiveThemeFile;
 
 void UXTHEME_InitSystem(HINSTANCE hInst);
 void UXTHEME_LoadTheme(BOOL bLoad);