Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
win: system menu: allow user remove menu items
Browse files Browse the repository at this point in the history
  • Loading branch information
wangwenx190 committed Oct 20, 2023
1 parent 2cb8ec0 commit 4821e89
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 24 deletions.
1 change: 1 addition & 0 deletions FramelessHelperConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ foreach(_component ${@PROJECT_NAME@_FIND_COMPONENTS})
if(EXISTS "${__targets_file}")
include("${__targets_file}")
add_library(${__target} ALIAS @PROJECT_NAME@::${__target_full})
list(APPEND _@PROJECT_NAME@_available_components ${_component})
else()
set(@PROJECT_NAME@_FOUND FALSE)
set(@PROJECT_NAME@_NOT_FOUND_MESSAGE "Can't find necessary configuration file for ${__target}, please make sure this component is built successfully and installed properly.")
Expand Down
9 changes: 9 additions & 0 deletions examples/dialog/dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
#include <FramelessHelper/Widgets/private/framelesswidgetshelper_p.h>
#include "../shared/settings.h"

extern template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
Expand Down Expand Up @@ -134,6 +135,14 @@ void Dialog::setupUi()
#if FRAMELESSHELPER_CONFIG(titlebar)
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(titleBar);
# ifdef Q_OS_WINDOWS
FramelessWidgetsHelperPrivate *helperPriv = FramelessWidgetsHelperPrivate::get(helper);
helperPriv->setProperty(kSysMenuRemoveRestoreVar, true);
helperPriv->setProperty(kSysMenuRemoveSizeVar, true);
helperPriv->setProperty(kSysMenuRemoveMinimizeVar, true);
helperPriv->setProperty(kSysMenuRemoveMaximizeVar, true);
helperPriv->setProperty(kSysMenuRemoveSeparatorVar, true);
# endif
# if (!defined(Q_OS_MACOS) && FRAMELESSHELPER_CONFIG(system_button))
helper->setSystemButton(titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(titleBar->maximizeButton(), SystemButtonType::Maximize);
Expand Down
10 changes: 10 additions & 0 deletions include/FramelessHelper/Core/framelesshelpercore_global.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,19 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API)

[[maybe_unused]] inline constexpr const char kDontOverrideCursorVar[] = "FRAMELESSHELPER_DONT_OVERRIDE_CURSOR";
[[maybe_unused]] inline constexpr const char kDontToggleMaximizeVar[] = "FRAMELESSHELPER_DONT_TOGGLE_MAXIMIZE";
[[maybe_unused]] inline constexpr const char kSysMenuDisableMoveVar[] = "FRAMELESSHELPER_SYSTEM_MENU_DISABLE_MOVE";
[[maybe_unused]] inline constexpr const char kSysMenuDisableSizeVar[] = "FRAMELESSHELPER_SYSTEM_MENU_DISABLE_SIZE";
[[maybe_unused]] inline constexpr const char kSysMenuDisableMinimizeVar[] = "FRAMELESSHELPER_SYSTEM_MENU_DISABLE_MINIMIZE";
[[maybe_unused]] inline constexpr const char kSysMenuDisableMaximizeVar[] = "FRAMELESSHELPER_SYSTEM_MENU_DISABLE_MAXIMIZE";
[[maybe_unused]] inline constexpr const char kSysMenuDisableRestoreVar[] = "FRAMELESSHELPER_SYSTEM_MENU_DISABLE_RESTORE";
[[maybe_unused]] inline constexpr const char kSysMenuDisableCloseVar[] = "FRAMELESSHELPER_SYSTEM_MENU_DISABLE_CLOSE";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveMoveVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_MOVE";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveSizeVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_SIZE";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveMinimizeVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_MINIMIZE";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveMaximizeVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_MAXIMIZE";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveRestoreVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_RESTORE";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveSeparatorVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_SEPARATOR";
[[maybe_unused]] inline constexpr const char kSysMenuRemoveCloseVar[] = "FRAMELESSHELPER_SYSTEM_MENU_REMOVE_CLOSE";

enum class Option : quint8
{
Expand Down
115 changes: 91 additions & 24 deletions src/core/utils_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ FRAMELESSHELPER_STRING_CONSTANT(EnableMenuItem)
FRAMELESSHELPER_STRING_CONSTANT(SetMenuDefaultItem)
FRAMELESSHELPER_STRING_CONSTANT(HiliteMenuItem)
FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
FRAMELESSHELPER_STRING_CONSTANT(DrawMenuBar)
FRAMELESSHELPER_STRING_CONSTANT(DeleteMenu)
FRAMELESSHELPER_STRING_CONSTANT(RemoveMenu)
FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen)
FRAMELESSHELPER_STRING_CONSTANT(DwmEnableBlurBehindWindow)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowCompositionAttribute)
Expand Down Expand Up @@ -1315,49 +1318,113 @@ bool Utils::showSystemMenu(const WId windowId, const QPoint &pos, const bool sel
}

// Tweak the menu items according to the current window status and user settings.
const bool disableClose = data->callbacks->getProperty(kSysMenuDisableCloseVar, false).toBool();
const bool disableRestore = data->callbacks->getProperty(kSysMenuDisableRestoreVar, false).toBool();
const bool disableMinimize = data->callbacks->getProperty(kSysMenuDisableMinimizeVar, false).toBool();
const bool disableMaximize = data->callbacks->getProperty(kSysMenuDisableMaximizeVar, false).toBool();
const bool disableSize = data->callbacks->getProperty(kSysMenuDisableSizeVar, false).toBool();
const bool disableMove = data->callbacks->getProperty(kSysMenuDisableMoveVar, false).toBool();
const bool removeClose = data->callbacks->getProperty(kSysMenuRemoveCloseVar, false).toBool();
const bool removeSeparator = data->callbacks->getProperty(kSysMenuRemoveSeparatorVar, false).toBool();
const bool removeRestore = data->callbacks->getProperty(kSysMenuRemoveRestoreVar, false).toBool();
const bool removeMinimize = data->callbacks->getProperty(kSysMenuRemoveMinimizeVar, false).toBool();
const bool removeMaximize = data->callbacks->getProperty(kSysMenuRemoveMaximizeVar, false).toBool();
const bool removeSize = data->callbacks->getProperty(kSysMenuRemoveSizeVar, false).toBool();
const bool removeMove = data->callbacks->getProperty(kSysMenuRemoveMoveVar, false).toBool();
const bool maxOrFull = (IsMaximized(hWnd) || isFullScreen(windowId));
const bool fixedSize = data->callbacks->isWindowFixedSize();
::EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize && !disableRestore) ? MFS_ENABLED : MFS_DISABLED)));
// The first menu item should be selected by default if the menu is brought
// up by keyboard. I don't know how to pre-select a menu item but it seems
// highlight can do the job. However, there's an annoying issue if we do
// this manually: the highlighted menu item is really only highlighted,
// not selected, so even if the mouse cursor hovers on other menu items
// or the user navigates to other menu items through keyboard, the original
// highlight bar will not move accordingly, the OS will generate another
// highlight bar to indicate the current selected menu item, which will make
// the menu look kind of weird. Currently I don't know how to fix this issue.
::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE)));
::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (!maxOrFull ? MFS_ENABLED : MFS_DISABLED)));
::EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize && !(disableMinimize || disableMaximize)) ? MFS_ENABLED : MFS_DISABLED)));
::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | (disableMinimize ? MFS_DISABLED : MFS_ENABLED)));
::EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize && !disableMaximize) ? MFS_ENABLED : MFS_DISABLED)));
::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | MFS_ENABLED));
if (removeClose) {
if (::DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
} else {
::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | (disableClose ? MFS_DISABLED : MFS_ENABLED)));
}
if (removeSeparator) {
if (::DeleteMenu(hMenu, SC_SEPARATOR, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
}
if (removeMaximize) {
if (::DeleteMenu(hMenu, SC_MAXIMIZE, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
} else {
::EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize && !disableMaximize) ? MFS_ENABLED : MFS_DISABLED)));
}
if (removeRestore) {
if (::DeleteMenu(hMenu, SC_RESTORE, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
} else {
::EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize && !disableRestore) ? MFS_ENABLED : MFS_DISABLED)));
// The first menu item should be selected by default if the menu is brought
// up by keyboard. I don't know how to pre-select a menu item but it seems
// highlight can do the job. However, there's an annoying issue if we do
// this manually: the highlighted menu item is really only highlighted,
// not selected, so even if the mouse cursor hovers on other menu items
// or the user navigates to other menu items through keyboard, the original
// highlight bar will not move accordingly, the OS will generate another
// highlight bar to indicate the current selected menu item, which will make
// the menu look kind of weird. Currently I don't know how to fix this issue.
::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE)));
}
if (removeMinimize) {
if (::DeleteMenu(hMenu, SC_MINIMIZE, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
} else {
::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | (disableMinimize ? MFS_DISABLED : MFS_ENABLED)));
}
if (removeSize) {
if (::DeleteMenu(hMenu, SC_SIZE, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
} else {
::EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize && !(disableSize || disableMinimize || disableMaximize)) ? MFS_ENABLED : MFS_DISABLED)));
}
if (removeMove) {
if (::DeleteMenu(hMenu, SC_MOVE, MF_BYCOMMAND) == FALSE) {
//WARNING << getSystemErrorMessage(kDeleteMenu);
}
} else {
::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | ((disableMove || maxOrFull) ? MFS_DISABLED : MFS_ENABLED)));
}

// The default menu item will appear in bold font. There can only be one default
// menu item per menu at most. Set the item ID to "UINT_MAX" (or simply "-1")
// can clear the default item for the given menu.
UINT defaultItemId = UINT_MAX;
std::optional<UINT> defaultItemId = std::nullopt;
if (WindowsVersionHelper::isWin11OrGreater()) {
if (maxOrFull) {
defaultItemId = SC_RESTORE;
if (!removeRestore) {
defaultItemId = SC_RESTORE;
}
} else {
defaultItemId = SC_MAXIMIZE;
if (!removeMaximize) {
defaultItemId = SC_MAXIMIZE;
}
}
} else {
}
if (!(defaultItemId.has_value() || removeClose)) {
defaultItemId = SC_CLOSE;
}
::SetMenuDefaultItem(hMenu, defaultItemId, FALSE);
if (::SetMenuDefaultItem(hMenu, defaultItemId.value_or(UINT_MAX), FALSE) == FALSE) {
WARNING << getSystemErrorMessage(kSetMenuDefaultItem);
}

if (::DrawMenuBar(hWnd) == FALSE) {
WARNING << getSystemErrorMessage(kDrawMenuBar);
}

// Popup the system menu at the required position.
const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), pos.x(), pos.y(), 0, hWnd, nullptr);

// Unhighlight the first menu item after the popup menu is closed, otherwise it will keep
// highlighting until we unhighlight it manually.
::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE));
if (!removeRestore) {
// Unhighlight the first menu item after the popup menu is closed, otherwise it will keep
// highlighting until we unhighlight it manually.
::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE));
}

if (result == FALSE) {
// The user canceled the menu, no need to continue.
Expand Down

0 comments on commit 4821e89

Please sign in to comment.