https://git.reactos.org/?p=reactos.git;a=commitdiff;h=26efda4d8ded94838cbcb3...
commit 26efda4d8ded94838cbcb330d67b87cdcdd3e334 Author: Katayama Hirofumi MZ katayama.hirofumi.mz@gmail.com AuthorDate: Thu Sep 22 12:18:59 2022 +0900 Commit: GitHub noreply@github.com CommitDate: Thu Sep 22 12:18:59 2022 +0900
[EXPLORER] Add 'Show Desktop' button at right edge of taskbar (#4715)
This PR adds a tiny button of window class "TrayShowDesktopButtonWClass" at right/bottom edge of taskbar. This button allows the user to access "Show/Restore Desktop" feature by mouse. You can toggle visibility of this button by registry value "TaskbarSd" in key "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced". The button is themed when theme is available. CORE-15369 --- base/shell/explorer/traywnd.cpp | 309 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 297 insertions(+), 12 deletions(-)
diff --git a/base/shell/explorer/traywnd.cpp b/base/shell/explorer/traywnd.cpp index da62c75c269..f1ff6242182 100644 --- a/base/shell/explorer/traywnd.cpp +++ b/base/shell/explorer/traywnd.cpp @@ -2,7 +2,7 @@ * ReactOS Explorer * * Copyright 2006 - 2007 Thomas Weidenmueller w3seek@reactos.org - * Copyright 2018 Katayama Hirofumi MZ katayama.hirofumi.mz@gmail.com + * Copyright 2018-2022 Katayama Hirofumi MZ katayama.hirofumi.mz@gmail.com * * this library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -264,6 +264,200 @@ public:
};
+// This window class name is CONFIRMED on Win10 by WinHier. +static const WCHAR szTrayShowDesktopButton[] = L"TrayShowDesktopButtonWClass"; + +// The 'Show Desktop' button at edge of taskbar +class CTrayShowDesktopButton : + public CWindowImpl<CTrayShowDesktopButton, CWindow, CControlWinTraits> +{ + LONG m_nClickedTime; + BOOL m_bHovering; + HTHEME m_hTheme; + +public: + DECLARE_WND_CLASS_EX(szTrayShowDesktopButton, CS_HREDRAW | CS_VREDRAW, COLOR_3DFACE) + + CTrayShowDesktopButton() : m_nClickedTime(0), m_bHovering(FALSE) + { + } + + INT WidthOrHeight() const + { +#define SHOW_DESKTOP_MINIMUM_WIDTH 3 + INT cxy = 2 * ::GetSystemMetrics(SM_CXEDGE); + return max(cxy, SHOW_DESKTOP_MINIMUM_WIDTH); + } + + HRESULT DoCreate(HWND hwndParent) + { + DWORD style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; + Create(hwndParent, NULL, NULL, style); + if (!m_hWnd) + return E_FAIL; + + ::SetWindowTheme(m_hWnd, L"TaskBar", NULL); + return S_OK; + } + + LRESULT OnClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + // The actual action can be delayed as an expected behaviour. + // But a too late action is an unexpected behaviour. + LONG nTime0 = m_nClickedTime; + LONG nTime1 = ::GetMessageTime(); + if (nTime1 - nTime0 >= 600) // Ignore after 0.6 sec + return 0; + + // Show/Hide Desktop + ::SendMessageW(::GetParent(m_hWnd), WM_COMMAND, TRAYCMD_TOGGLE_DESKTOP, 0); + return 0; + } + +#define TSDB_CLICK (WM_USER + 100) + + // This function is called from OnLButtonDown and parent. + VOID Click() + { + // The actual action can be delayed as an expected behaviour. + m_nClickedTime = ::GetMessageTime(); + PostMessage(TSDB_CLICK, 0, 0); + } + + LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + Click(); // Left-click + return 0; + } + + LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if (m_hTheme) + ::CloseThemeData(m_hTheme); + + m_hTheme = ::OpenThemeData(m_hWnd, L"TaskBar"); + InvalidateRect(NULL, TRUE); + return 0; + } + + // This function is called from OnPaint and parent. + VOID OnDraw(HDC hdc, LPRECT prc); + + LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + RECT rc; + GetClientRect(&rc); + + PAINTSTRUCT ps; + HDC hdc = BeginPaint(&ps); + OnDraw(hdc, &rc); + EndPaint(&ps); + return 0; + } + + BOOL PtInButton(POINT pt) + { + RECT rc; + GetWindowRect(&rc); + INT cxEdge = ::GetSystemMetrics(SM_CXEDGE), cyEdge = ::GetSystemMetrics(SM_CYEDGE); + ::InflateRect(&rc, max(cxEdge, 1), max(cyEdge, 1)); + return ::PtInRect(&rc, pt); + } + +#define SHOW_DESKTOP_TIMER_ID 999 +#define SHOW_DESKTOP_TIMER_INTERVAL 200 + + VOID StartHovering() + { + if (m_bHovering) + return; + + m_bHovering = TRUE; + SetTimer(SHOW_DESKTOP_TIMER_ID, SHOW_DESKTOP_TIMER_INTERVAL, NULL); + InvalidateRect(NULL, TRUE); + ::SendMessageW(::GetParent(m_hWnd), WM_NCPAINT, 0, 0); + } + + LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + StartHovering(); + return 0; + } + + LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if (wParam != SHOW_DESKTOP_TIMER_ID || !m_bHovering) + return 0; + + POINT pt; + ::GetCursorPos(&pt); + if (!PtInButton(pt)) // The end of hovering? + { + m_bHovering = FALSE; + KillTimer(SHOW_DESKTOP_TIMER_ID); + InvalidateRect(NULL, TRUE); + ::SendMessageW(::GetParent(m_hWnd), WM_NCPAINT, 0, 0); + } + + return 0; + } + + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + if (m_hTheme) + { + CloseThemeData(m_hTheme); + m_hTheme = NULL; + } + return 0; + } + + BEGIN_MSG_MAP(CTrayShowDesktopButton) + MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) + MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) + MESSAGE_HANDLER(WM_THEMECHANGED, OnSettingChanged) + MESSAGE_HANDLER(WM_PAINT, OnPaint) + MESSAGE_HANDLER(WM_TIMER, OnTimer) + MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) + MESSAGE_HANDLER(WM_DESTROY, OnDestroy) + MESSAGE_HANDLER(TSDB_CLICK, OnClick) + END_MSG_MAP() +}; + +VOID CTrayShowDesktopButton::OnDraw(HDC hdc, LPRECT prc) +{ + if (m_hTheme) + { + if (m_bHovering) // Draw a hot button + { + HTHEME hButtonTheme = ::OpenThemeData(m_hWnd, L"Button"); + ::DrawThemeBackground(hButtonTheme, hdc, BP_PUSHBUTTON, PBS_NORMAL, prc, prc); + ::CloseThemeData(hButtonTheme); + } + else // Draw a taskbar background + { + ::DrawThemeBackground(m_hTheme, hdc, TBP_BACKGROUNDTOP, 0, prc, prc); + } + } + else + { + RECT rc = *prc; + if (m_bHovering) // Draw a hot button + { + ::DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_ADJUSTRECT); + HBRUSH hbrHot = ::CreateSolidBrush(RGB(255, 255, 191)); + ::FillRect(hdc, &rc, hbrHot); + ::DeleteObject(hbrHot); + } + else // Draw a flattish button + { + ::DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH); + ::InflateRect(&rc, -1, -1); + ::FillRect(hdc, &rc, ::GetSysColorBrush(COLOR_3DFACE)); + } + } +} + class CTrayWindow : public CComCoClass<CTrayWindow>, public CComObjectRootEx<CComMultiThreadModelNoCS>, @@ -274,6 +468,7 @@ class CTrayWindow : public IContextMenu { CStartButton m_StartButton; + CTrayShowDesktopButton m_ShowDesktopButton;
CComPtr<IMenuBand> m_StartMenuBand; CComPtr<IMenuPopup> m_StartMenuPopup; @@ -327,6 +522,7 @@ public: public: CTrayWindow() : m_StartButton(), + m_ShowDesktopButton(), m_Theme(NULL), m_Font(NULL), m_DesktopWnd(NULL), @@ -1617,7 +1813,7 @@ ChangePos:
/* We're about to resize/move the start button, the rebar control and the tray notification control */ - dwp = BeginDeferWindowPos(3); + dwp = BeginDeferWindowPos(4); if (dwp == NULL) { ERR("BeginDeferWindowPos failed. lastErr=%d\n", GetLastError()); @@ -1655,6 +1851,35 @@ ChangePos: } }
+ if (m_ShowDesktopButton.m_hWnd) + { + // Get rectangle from rcClient + RECT rc = rcClient; + INT cxyShowDesktop = m_ShowDesktopButton.WidthOrHeight(); + if (Horizontal) + { + rc.left = rc.right - cxyShowDesktop; + rc.right += 5; // excessive + } + else + { + rc.top = rc.bottom - cxyShowDesktop; + rc.bottom += 5; // excessive + } + + /* Resize and reposition the button */ + dwp = m_ShowDesktopButton.DeferWindowPos(dwp, NULL, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + SWP_NOZORDER | SWP_NOACTIVATE); + + // Adjust rcClient + if (Horizontal) + rcClient.right -= cxyShowDesktop + ::GetSystemMetrics(SM_CXEDGE); + else + rcClient.bottom -= cxyShowDesktop + ::GetSystemMetrics(SM_CYEDGE); + } + /* Determine the size that the tray notification window needs */ if (Horizontal) { @@ -2209,6 +2434,15 @@ ChangePos: return m_ContextMenu->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax); }
+ BOOL IsShowDesktopButtonNeeded() // Read the registry value + { + return SHRegGetBoolUSValueW( + L"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", + L"TaskbarSd", + FALSE, + TRUE); + } + /********************************************************** * ##### message handling ##### */ @@ -2224,6 +2458,10 @@ ChangePos: /* Create the Start button */ m_StartButton.Create(m_hWnd);
+ /* Create the 'Show Desktop' button if necessary */ + if (IsShowDesktopButtonNeeded()) + m_ShowDesktopButton.DoCreate(m_hWnd); + /* Load the saved tray window settings */ RegLoadSettings();
@@ -2364,19 +2602,34 @@ ChangePos: return FALSE; }
+ // We have to draw non-client area because the 'Show Desktop' button is beyond client area. + void DrawShowDesktopButton() + { + // Get the rectangle in window coordinates + RECT rcButton, rcWnd; + GetWindowRect(&rcWnd); + m_ShowDesktopButton.GetWindowRect(&rcButton); + ::OffsetRect(&rcButton, -rcWnd.left, -rcWnd.top); + + HDC hdc = GetDCEx(NULL, DCX_WINDOW | DCX_CACHE | DCX_NORESETATTRS); + m_ShowDesktopButton.OnDraw(hdc, &rcButton); // Draw the button + ReleaseDC(hdc); + } + LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { - if (!m_Theme) - { - bHandled = FALSE; - return 0; - } - else if (g_TaskbarSettings.bLock) + DefWindowProc(uMsg, wParam, lParam); + bHandled = TRUE; + + if (!m_Theme || g_TaskbarSettings.bLock) { + DrawShowDesktopButton(); // We have to draw non-client area return 0; }
- return DrawSizerWithTheme((HRGN) wParam); + DrawSizerWithTheme((HRGN) wParam); + DrawShowDesktopButton(); // We have to draw non-client area + return 0; }
LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) @@ -2601,11 +2854,15 @@ ChangePos:
POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; WINDOWINFO wi = {sizeof(WINDOWINFO)}; - RECT rcStartBtn;
bHandled = FALSE;
+ if (CheckShowDesktopButtonClick(lParam, bHandled)) + return TRUE; + + RECT rcStartBtn; m_StartButton.GetWindowRect(&rcStartBtn); + GetWindowInfo(m_hWnd, &wi);
switch (m_Position) @@ -2632,9 +2889,7 @@ ChangePos: case ABE_BOTTOM: { if (pt.x > rcStartBtn.right || pt.y < rcStartBtn.top) - { return 0; - }
if (rcStartBtn.bottom + (int)wi.cyWindowBorders * 2 + 1 < wi.rcWindow.bottom && pt.y > rcStartBtn.bottom) @@ -2764,8 +3019,24 @@ HandleTrayContextMenu: return Ret; }
+ BOOL CheckShowDesktopButtonClick(LPARAM lParam, BOOL& bHandled) + { + POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + if (m_ShowDesktopButton.PtInButton(pt)) // Did you click the button? + { + m_ShowDesktopButton.Click(); + bHandled = TRUE; + return TRUE; + } + + return FALSE; + } + LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { + if (CheckShowDesktopButtonClick(lParam, bHandled)) + return TRUE; + /* Let the clock handle the double click */ ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
@@ -2931,6 +3202,11 @@ HandleTrayContextMenu:
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { + POINT pt; + ::GetCursorPos(&pt); + if (m_ShowDesktopButton.PtInButton(pt)) + m_ShowDesktopButton.StartHovering(); + if (g_TaskbarSettings.sr.AutoHide) { SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL); @@ -2954,6 +3230,14 @@ HandleTrayContextMenu: return TRUE; }
+ LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + DefWindowProc(uMsg, wParam, lParam); + DrawShowDesktopButton(); // We have to draw non-client area + bHandled = TRUE; + return 0; + } + LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { RECT *rc = NULL; @@ -3124,6 +3408,7 @@ HandleTrayContextMenu: MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange) MESSAGE_HANDLER(WM_COPYDATA, OnCopyData) MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint) + MESSAGE_HANDLER(WM_NCACTIVATE, OnNcActivate) MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn) MESSAGE_HANDLER(WM_MOVING, OnMoving) MESSAGE_HANDLER(WM_SIZING, OnSizing)