https://git.reactos.org/?p=reactos.git;a=commitdiff;h=26efda4d8ded94838cbcb…
commit 26efda4d8ded94838cbcb330d67b87cdcdd3e334
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Thu Sep 22 12:18:59 2022 +0900
Commit: GitHub <noreply(a)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(a)reactos.org>
- * Copyright 2018 Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
+ * Copyright 2018-2022 Katayama Hirofumi MZ <katayama.hirofumi.mz(a)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)