mirror of
https://github.com/reactos/reactos.git
synced 2025-05-20 17:45:06 +00:00
[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
This commit is contained in:
parent
34635e1585
commit
26efda4d8d
1 changed files with 297 additions and 12 deletions
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue