2016-06-16 12:59:54 +00:00
/*
This file is part of Telegram Desktop ,
2018-01-03 10:23:14 +00:00
the official desktop application for the Telegram messaging service .
2016-06-16 12:59:54 +00:00
2018-01-03 10:23:14 +00:00
For license and copyright information please follow this link :
https : //github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2016-06-16 12:59:54 +00:00
*/
# include "platform/win/main_window_win.h"
2016-11-04 08:23:50 +00:00
# include "styles/style_window.h"
2021-01-10 03:52:18 +00:00
# include "platform/platform_specific.h"
2016-10-03 15:07:50 +00:00
# include "platform/platform_notifications_manager.h"
2016-06-16 12:59:54 +00:00
# include "platform/win/windows_dlls.h"
2019-06-07 11:30:30 +00:00
# include "platform/win/windows_event_filter.h"
2016-10-02 13:54:27 +00:00
# include "window/notifications_manager.h"
2016-06-16 12:59:54 +00:00
# include "mainwindow.h"
2019-09-16 11:14:06 +00:00
# include "base/crc32hash.h"
2021-01-16 12:33:09 +00:00
# include "base/platform/win/base_windows_wrl.h"
2019-01-21 13:42:21 +00:00
# include "core/application.h"
2017-04-13 08:27:10 +00:00
# include "lang/lang_keys.h"
2017-03-04 10:23:56 +00:00
# include "storage/localstorage.h"
2016-10-26 16:43:13 +00:00
# include "ui/widgets/popup_menu.h"
2017-02-03 20:07:26 +00:00
# include "window/themes/window_theme.h"
2018-01-13 12:45:11 +00:00
# include "history/history.h"
2019-09-13 06:06:02 +00:00
# include "app.h"
2016-06-16 12:59:54 +00:00
2019-09-04 07:19:15 +00:00
# include <QtWidgets/QDesktopWidget>
# include <QtWidgets/QStyleFactory>
# include <QtWidgets/QApplication>
# include <QtGui/QWindow>
2021-01-16 12:33:09 +00:00
# include <QtGui/QScreen>
2016-06-16 12:59:54 +00:00
# include <qpa/qplatformnativeinterface.h>
# include <Shobjidl.h>
# include <shellapi.h>
# include <WtsApi32.h>
2021-01-16 12:33:09 +00:00
# include <windows.ui.viewmanagement.h>
# include <UIViewSettingsInterop.h>
2016-06-16 12:59:54 +00:00
# include <Windowsx.h>
2019-06-03 10:48:36 +00:00
# include <VersionHelpers.h>
2016-06-16 12:59:54 +00:00
HICON qt_pixmapToWinHICON ( const QPixmap & ) ;
2020-05-09 18:07:18 +00:00
Q_DECLARE_METATYPE ( QMargins ) ;
2021-01-16 12:33:09 +00:00
namespace ViewManagement = ABI : : Windows : : UI : : ViewManagement ;
2016-06-16 12:59:54 +00:00
namespace Platform {
namespace {
2021-01-22 12:16:18 +00:00
// Mouse down on tray icon deactivates the application.
// So there is no way to know for sure if the tray icon was clicked from
// active application or from inactive application. So we assume that
// if the application was deactivated less than 0.5s ago, then the tray
// icon click (both left or right button) was made from the active app.
constexpr auto kKeepActiveForTrayIcon = crl : : time ( 500 ) ;
2021-01-16 12:33:09 +00:00
using namespace Microsoft : : WRL ;
2016-06-16 12:59:54 +00:00
HICON createHIconFromQIcon ( const QIcon & icon , int xSize , int ySize ) {
if ( ! icon . isNull ( ) ) {
const QPixmap pm = icon . pixmap ( icon . actualSize ( QSize ( xSize , ySize ) ) ) ;
if ( ! pm . isNull ( ) ) {
return qt_pixmapToWinHICON ( pm ) ;
}
}
return nullptr ;
}
HWND createTaskbarHider ( ) {
HINSTANCE appinst = ( HINSTANCE ) GetModuleHandle ( 0 ) ;
HWND hWnd = 0 ;
QString cn = QString ( " TelegramTaskbarHider " ) ;
LPCWSTR _cn = ( LPCWSTR ) cn . utf16 ( ) ;
WNDCLASSEX wc ;
wc . cbSize = sizeof ( wc ) ;
wc . style = 0 ;
wc . lpfnWndProc = DefWindowProc ;
wc . cbClsExtra = 0 ;
wc . cbWndExtra = 0 ;
wc . hInstance = appinst ;
wc . hIcon = 0 ;
wc . hCursor = 0 ;
wc . hbrBackground = 0 ;
wc . lpszMenuName = NULL ;
wc . lpszClassName = _cn ;
wc . hIconSm = 0 ;
if ( ! RegisterClassEx ( & wc ) ) {
DEBUG_LOG ( ( " Application Error: could not register taskbar hider window class, error: %1 " ) . arg ( GetLastError ( ) ) ) ;
return hWnd ;
}
hWnd = CreateWindowEx ( WS_EX_TOOLWINDOW , _cn , 0 , WS_POPUP , 0 , 0 , 0 , 0 , 0 , 0 , appinst , 0 ) ;
if ( ! hWnd ) {
DEBUG_LOG ( ( " Application Error: could not create taskbar hider window class, error: %1 " ) . arg ( GetLastError ( ) ) ) ;
return hWnd ;
}
return hWnd ;
}
ComPtr < ITaskbarList3 > taskbarList ;
bool handleSessionNotification = false ;
2021-01-16 12:33:09 +00:00
uint32 kTaskbarCreatedMsgId = 0 ;
2016-06-16 12:59:54 +00:00
} // namespace
2021-01-16 12:33:09 +00:00
struct MainWindow : : Private {
ComPtr < ViewManagement : : IUIViewSettings > viewSettings ;
} ;
2016-06-16 12:59:54 +00:00
2019-06-06 11:20:21 +00:00
MainWindow : : MainWindow ( not_null < Window : : Controller * > controller )
: Window : : MainWindow ( controller )
2021-01-16 12:33:09 +00:00
, _private ( std : : make_unique < Private > ( ) )
2019-06-06 11:20:21 +00:00
, ps_tbHider_hWnd ( createTaskbarHider ( ) ) {
2019-06-07 11:30:30 +00:00
QCoreApplication : : instance ( ) - > installNativeEventFilter (
EventFilter : : CreateInstance ( this ) ) ;
2021-01-16 12:33:09 +00:00
if ( ! kTaskbarCreatedMsgId ) {
kTaskbarCreatedMsgId = RegisterWindowMessage ( L " TaskbarButtonCreated " ) ;
2016-06-16 12:59:54 +00:00
}
2016-12-31 13:34:41 +00:00
subscribe ( Window : : Theme : : Background ( ) , [ this ] ( const Window : : Theme : : BackgroundUpdate & update ) {
2020-07-07 08:17:06 +00:00
if ( _shadow & & update . paletteChanged ( ) ) {
_shadow - > setColor ( st : : windowShadowFg - > c ) ;
2016-12-23 13:21:01 +00:00
}
} ) ;
2020-07-24 12:15:32 +00:00
setupNativeWindowFrame ( ) ;
2021-01-22 12:16:18 +00:00
using namespace rpl : : mappers ;
Core : : App ( ) . appDeactivatedValue (
) | rpl : : distinct_until_changed (
) | rpl : : filter ( _1 ) | rpl : : start_with_next ( [ = ] {
_lastDeactivateTime = crl : : now ( ) ;
} , lifetime ( ) ) ;
2020-07-24 12:15:32 +00:00
}
void MainWindow : : setupNativeWindowFrame ( ) {
auto nativeFrame = rpl : : single (
Core : : App ( ) . settings ( ) . nativeWindowFrame ( )
) | rpl : : then (
Core : : App ( ) . settings ( ) . nativeWindowFrameChanges ( )
) ;
using BackgroundUpdate = Window : : Theme : : BackgroundUpdate ;
auto paletteChanges = base : : ObservableViewer (
* Window : : Theme : : Background ( )
) | rpl : : filter ( [ = ] ( const BackgroundUpdate & update ) {
return update . type = = BackgroundUpdate : : Type : : ApplyingTheme ;
} ) | rpl : : to_empty ;
auto nightMode = rpl : : single (
rpl : : empty_value ( )
) | rpl : : then (
std : : move ( paletteChanges )
) | rpl : : map ( [ = ] {
return Window : : Theme : : IsNightMode ( ) ;
} ) | rpl : : distinct_until_changed ( ) ;
rpl : : combine (
std : : move ( nativeFrame ) ,
std : : move ( nightMode )
) | rpl : : skip ( 1 ) | rpl : : start_with_next ( [ = ] ( bool native , bool night ) {
const auto nativeChanged = ( _wasNativeFrame ! = native ) ;
if ( nativeChanged ) {
_wasNativeFrame = native ;
initShadows ( ) ;
}
validateWindowTheme ( native , night ) ;
if ( nativeChanged ) {
fixMaximizedWindow ( ) ;
}
2020-07-07 13:54:39 +00:00
} , lifetime ( ) ) ;
2016-06-16 12:59:54 +00:00
}
2021-01-16 12:33:09 +00:00
uint32 MainWindow : : TaskbarCreatedMsgId ( ) {
return kTaskbarCreatedMsgId ;
}
2016-06-16 12:59:54 +00:00
void MainWindow : : TaskbarCreated ( ) {
HRESULT hr = CoCreateInstance ( CLSID_TaskbarList , nullptr , CLSCTX_ALL , IID_PPV_ARGS ( & taskbarList ) ) ;
if ( ! SUCCEEDED ( hr ) ) {
taskbarList . Reset ( ) ;
}
}
2020-07-07 08:17:06 +00:00
void MainWindow : : shadowsUpdate (
Ui : : Platform : : WindowShadow : : Changes changes ,
WINDOWPOS * position ) {
if ( _shadow ) {
_shadow - > update ( changes , position ) ;
}
2016-06-16 12:59:54 +00:00
}
void MainWindow : : shadowsActivate ( ) {
2020-07-24 12:15:32 +00:00
_hasActiveFrame = true ;
2020-07-07 08:17:06 +00:00
// _shadow->setColor(_shActive);
shadowsUpdate ( Ui : : Platform : : WindowShadow : : Change : : Activate ) ;
2016-06-16 12:59:54 +00:00
}
void MainWindow : : shadowsDeactivate ( ) {
2020-07-24 12:15:32 +00:00
_hasActiveFrame = false ;
2020-07-07 08:17:06 +00:00
// _shadow->setColor(_shInactive);
2016-06-16 12:59:54 +00:00
}
void MainWindow : : psShowTrayMenu ( ) {
trayIconMenu - > popup ( QCursor : : pos ( ) ) ;
}
2016-11-04 11:14:47 +00:00
int32 MainWindow : : screenNameChecksum ( const QString & name ) const {
constexpr int DeviceNameSize = base : : array_size ( MONITORINFOEX ( ) . szDevice ) ;
wchar_t buffer [ DeviceNameSize ] = { 0 } ;
if ( name . size ( ) < DeviceNameSize ) {
name . toWCharArray ( buffer ) ;
} else {
memcpy ( buffer , name . toStdWString ( ) . data ( ) , sizeof ( buffer ) ) ;
}
2019-09-16 11:14:06 +00:00
return base : : crc32 ( buffer , sizeof ( buffer ) ) ;
2016-11-04 11:14:47 +00:00
}
2016-06-16 12:59:54 +00:00
void MainWindow : : psRefreshTaskbarIcon ( ) {
2018-07-12 19:25:10 +00:00
const auto refresher = std : : make_unique < QWidget > ( this ) ;
2017-01-16 13:27:11 +00:00
refresher - > setWindowFlags ( static_cast < Qt : : WindowFlags > ( Qt : : Tool ) | Qt : : FramelessWindowHint ) ;
refresher - > setGeometry ( x ( ) + 1 , y ( ) + 1 , 1 , 1 ) ;
auto palette = refresher - > palette ( ) ;
2020-11-04 15:50:17 +00:00
palette . setColor ( QPalette : : Window , ( isActiveWindow ( ) ? st : : titleBgActive : st : : titleBg ) - > c ) ;
2017-01-16 13:27:11 +00:00
refresher - > setPalette ( palette ) ;
refresher - > show ( ) ;
refresher - > activateWindow ( ) ;
updateIconCounters ( ) ;
2016-06-16 12:59:54 +00:00
}
void MainWindow : : psTrayMenuUpdated ( ) {
}
void MainWindow : : psSetupTrayIcon ( ) {
if ( ! trayIcon ) {
trayIcon = new QSystemTrayIcon ( this ) ;
2019-01-21 13:42:21 +00:00
auto icon = QIcon ( App : : pixmapFromImageInPlace ( Core : : App ( ) . logoNoMargin ( ) ) ) ;
2016-06-16 12:59:54 +00:00
trayIcon - > setIcon ( icon ) ;
2020-12-27 18:45:30 +00:00
connect (
trayIcon ,
& QSystemTrayIcon : : messageClicked ,
this ,
[ = ] { App : : wnd ( ) - > showFromTray ( ) ; } ) ;
2019-04-12 13:21:01 +00:00
attachToTrayIcon ( trayIcon ) ;
2016-06-16 12:59:54 +00:00
}
2016-11-09 08:34:38 +00:00
updateIconCounters ( ) ;
2016-06-16 12:59:54 +00:00
trayIcon - > show ( ) ;
}
2017-01-01 16:45:20 +00:00
void MainWindow : : showTrayTooltip ( ) {
if ( trayIcon & & ! cSeenTrayTooltip ( ) ) {
2020-01-29 09:44:37 +00:00
trayIcon - > showMessage (
AppName . utf16 ( ) ,
tr : : lng_tray_icon_text ( tr : : now ) ,
QSystemTrayIcon : : Information ,
10000 ) ;
2017-01-01 16:45:20 +00:00
cSetSeenTrayTooltip ( true ) ;
Local : : writeSettings ( ) ;
}
}
2017-03-04 19:36:59 +00:00
void MainWindow : : workmodeUpdated ( DBIWorkMode mode ) {
switch ( mode ) {
2016-06-16 12:59:54 +00:00
case dbiwmWindowAndTray : {
psSetupTrayIcon ( ) ;
2020-05-09 18:07:18 +00:00
HWND psOwner = ( HWND ) GetWindowLongPtr ( ps_hWnd , GWLP_HWNDPARENT ) ;
2016-06-16 12:59:54 +00:00
if ( psOwner ) {
2020-05-09 18:07:18 +00:00
SetWindowLongPtr ( ps_hWnd , GWLP_HWNDPARENT , 0 ) ;
2016-06-16 12:59:54 +00:00
psRefreshTaskbarIcon ( ) ;
}
} break ;
case dbiwmTrayOnly : {
psSetupTrayIcon ( ) ;
2020-05-09 18:07:18 +00:00
HWND psOwner = ( HWND ) GetWindowLongPtr ( ps_hWnd , GWLP_HWNDPARENT ) ;
2016-06-16 12:59:54 +00:00
if ( ! psOwner ) {
2020-10-25 00:59:14 +00:00
SetWindowLongPtr ( ps_hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) ps_tbHider_hWnd ) ;
2016-06-16 12:59:54 +00:00
}
} break ;
case dbiwmWindowOnly : {
if ( trayIcon ) {
trayIcon - > setContextMenu ( 0 ) ;
trayIcon - > deleteLater ( ) ;
}
trayIcon = 0 ;
2020-05-09 18:07:18 +00:00
HWND psOwner = ( HWND ) GetWindowLongPtr ( ps_hWnd , GWLP_HWNDPARENT ) ;
2016-06-16 12:59:54 +00:00
if ( psOwner ) {
2020-05-09 18:07:18 +00:00
SetWindowLongPtr ( ps_hWnd , GWLP_HWNDPARENT , 0 ) ;
2016-06-16 12:59:54 +00:00
psRefreshTaskbarIcon ( ) ;
}
} break ;
}
}
2021-01-16 12:33:09 +00:00
bool MainWindow : : hasTabletView ( ) const {
if ( ! _private - > viewSettings ) {
return false ;
}
auto mode = ViewManagement : : UserInteractionMode ( ) ;
_private - > viewSettings - > get_UserInteractionMode ( & mode ) ;
return ( mode = = ViewManagement : : UserInteractionMode_Touch ) ;
}
bool MainWindow : : initSizeFromSystem ( ) {
if ( ! hasTabletView ( ) ) {
return false ;
}
const auto screen = [ & ] {
if ( const auto result = windowHandle ( ) - > screen ( ) ) {
return result ;
}
return QGuiApplication : : primaryScreen ( ) ;
} ( ) ;
if ( ! screen ) {
return false ;
}
2021-02-08 12:07:38 +00:00
setGeometry ( screen - > availableGeometry ( ) ) ;
2021-01-16 12:33:09 +00:00
return true ;
}
2020-07-07 13:54:39 +00:00
void MainWindow : : updateWindowIcon ( ) {
updateIconCounters ( ) ;
}
2021-01-22 12:16:18 +00:00
bool MainWindow : : isActiveForTrayMenu ( ) {
return ! _lastDeactivateTime
| | ( _lastDeactivateTime + kKeepActiveForTrayIcon > = crl : : now ( ) ) ;
}
2016-11-09 08:34:38 +00:00
void MainWindow : : unreadCounterChangedHook ( ) {
setWindowTitle ( titleText ( ) ) ;
updateIconCounters ( ) ;
}
void MainWindow : : updateIconCounters ( ) {
2019-01-21 13:42:21 +00:00
const auto counter = Core : : App ( ) . unreadBadge ( ) ;
const auto muted = Core : : App ( ) . unreadBadgeMuted ( ) ;
2016-06-16 12:59:54 +00:00
2016-10-20 14:46:16 +00:00
auto iconSizeSmall = QSize ( GetSystemMetrics ( SM_CXSMICON ) , GetSystemMetrics ( SM_CYSMICON ) ) ;
auto iconSizeBig = QSize ( GetSystemMetrics ( SM_CXICON ) , GetSystemMetrics ( SM_CYICON ) ) ;
2016-10-31 12:29:26 +00:00
auto & bg = ( muted ? st : : trayCounterBgMute : st : : trayCounterBg ) ;
auto & fg = st : : trayCounterFg ;
auto iconSmallPixmap16 = App : : pixmapFromImageInPlace ( iconWithCounter ( 16 , counter , bg , fg , true ) ) ;
auto iconSmallPixmap32 = App : : pixmapFromImageInPlace ( iconWithCounter ( 32 , counter , bg , fg , true ) ) ;
2016-06-16 12:59:54 +00:00
QIcon iconSmall , iconBig ;
2016-10-20 14:46:16 +00:00
iconSmall . addPixmap ( iconSmallPixmap16 ) ;
iconSmall . addPixmap ( iconSmallPixmap32 ) ;
2016-10-31 12:29:26 +00:00
iconBig . addPixmap ( App : : pixmapFromImageInPlace ( iconWithCounter ( 32 , taskbarList . Get ( ) ? 0 : counter , bg , fg , false ) ) ) ;
iconBig . addPixmap ( App : : pixmapFromImageInPlace ( iconWithCounter ( 64 , taskbarList . Get ( ) ? 0 : counter , bg , fg , false ) ) ) ;
2016-06-16 12:59:54 +00:00
if ( trayIcon ) {
2016-10-20 14:46:16 +00:00
// Force Qt to use right icon size, not the larger one.
QIcon forTrayIcon ;
2016-11-07 11:24:19 +00:00
forTrayIcon . addPixmap ( iconSizeSmall . width ( ) > = 20 ? iconSmallPixmap32 : iconSmallPixmap16 ) ;
2016-10-20 14:46:16 +00:00
trayIcon - > setIcon ( forTrayIcon ) ;
2016-06-16 12:59:54 +00:00
}
psDestroyIcons ( ) ;
2016-10-20 14:46:16 +00:00
ps_iconSmall = createHIconFromQIcon ( iconSmall , iconSizeSmall . width ( ) , iconSizeSmall . height ( ) ) ;
ps_iconBig = createHIconFromQIcon ( iconBig , iconSizeBig . width ( ) , iconSizeBig . height ( ) ) ;
2016-06-16 12:59:54 +00:00
SendMessage ( ps_hWnd , WM_SETICON , 0 , ( LPARAM ) ps_iconSmall ) ;
SendMessage ( ps_hWnd , WM_SETICON , 1 , ( LPARAM ) ( ps_iconBig ? ps_iconBig : ps_iconSmall ) ) ;
if ( taskbarList . Get ( ) ) {
if ( counter > 0 ) {
QIcon iconOverlay ;
2016-10-31 12:29:26 +00:00
iconOverlay . addPixmap ( App : : pixmapFromImageInPlace ( iconWithCounter ( - 16 , counter , bg , fg , false ) ) ) ;
iconOverlay . addPixmap ( App : : pixmapFromImageInPlace ( iconWithCounter ( - 32 , counter , bg , fg , false ) ) ) ;
2016-06-16 12:59:54 +00:00
ps_iconOverlay = createHIconFromQIcon ( iconOverlay , GetSystemMetrics ( SM_CXSMICON ) , GetSystemMetrics ( SM_CYSMICON ) ) ;
}
2019-06-19 16:39:25 +00:00
auto description = ( counter > 0 ) ? tr : : lng_unread_bar ( tr : : now , lt_count , counter ) : QString ( ) ;
2016-06-16 12:59:54 +00:00
taskbarList - > SetOverlayIcon ( ps_hWnd , ps_iconOverlay , description . toStdWString ( ) . c_str ( ) ) ;
}
SetWindowPos ( ps_hWnd , 0 , 0 , 0 , 0 , 0 , SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE ) ;
}
2016-11-04 11:14:47 +00:00
void MainWindow : : initHook ( ) {
2019-03-21 18:21:23 +00:00
if ( const auto native = QGuiApplication : : platformNativeInterface ( ) ) {
ps_hWnd = static_cast < HWND > ( native - > nativeResourceForWindow (
QByteArrayLiteral ( " handle " ) ,
windowHandle ( ) ) ) ;
}
if ( ! ps_hWnd ) {
return ;
}
2016-06-16 12:59:54 +00:00
2019-03-21 18:21:23 +00:00
handleSessionNotification = ( Dlls : : WTSRegisterSessionNotification ! = nullptr )
& & ( Dlls : : WTSUnRegisterSessionNotification ! = nullptr ) ;
2016-06-16 12:59:54 +00:00
if ( handleSessionNotification ) {
Dlls : : WTSRegisterSessionNotification ( ps_hWnd , NOTIFY_FOR_THIS_SESSION ) ;
}
2021-01-16 12:33:09 +00:00
using namespace base : : Platform ;
auto factory = ComPtr < IUIViewSettingsInterop > ( ) ;
if ( SupportsWRL ( ) ) {
GetActivationFactory (
StringReferenceWrapper (
RuntimeClass_Windows_UI_ViewManagement_UIViewSettings ) . Get ( ) ,
& factory ) ;
if ( factory ) {
factory - > GetForWindow (
ps_hWnd ,
IID_PPV_ARGS ( & _private - > viewSettings ) ) ;
}
}
2016-06-16 12:59:54 +00:00
psInitSysMenu ( ) ;
}
2020-02-12 08:09:17 +00:00
void MainWindow : : initShadows ( ) {
2020-07-07 13:54:39 +00:00
if ( Core : : App ( ) . settings ( ) . nativeWindowFrame ( ) ) {
_shadow . reset ( ) ;
} else {
_shadow . emplace ( this , st : : windowShadowFg - > c ) ;
}
updateCustomMargins ( ) ;
firstShadowsUpdate ( ) ;
2020-02-12 08:09:17 +00:00
}
2016-06-16 12:59:54 +00:00
2020-02-12 08:09:17 +00:00
void MainWindow : : firstShadowsUpdate ( ) {
2020-07-07 13:54:39 +00:00
using Change = Ui : : Platform : : WindowShadow : : Change ;
if ( ( windowState ( ) & ( Qt : : WindowMinimized | Qt : : WindowMaximized ) )
| | isHidden ( ) ) {
shadowsUpdate ( Change : : Hidden ) ;
} else {
2020-07-07 08:17:06 +00:00
shadowsUpdate ( Change : : Moved | Change : : Resized | Change : : Shown ) ;
2016-06-16 12:59:54 +00:00
}
}
2017-05-19 14:02:55 +00:00
void MainWindow : : stateChangedHook ( Qt : : WindowState state ) {
updateSystemMenu ( state ) ;
}
2016-06-16 12:59:54 +00:00
void MainWindow : : psInitSysMenu ( ) {
Qt : : WindowStates states = windowState ( ) ;
ps_menu = GetSystemMenu ( ps_hWnd , FALSE ) ;
2017-05-19 14:02:55 +00:00
updateSystemMenu ( windowHandle ( ) - > windowState ( ) ) ;
2016-06-16 12:59:54 +00:00
}
2017-05-19 14:02:55 +00:00
void MainWindow : : updateSystemMenu ( Qt : : WindowState state ) {
2016-06-16 12:59:54 +00:00
if ( ! ps_menu ) return ;
int menuToDisable = SC_RESTORE ;
if ( state = = Qt : : WindowMaximized ) {
menuToDisable = SC_MAXIMIZE ;
} else if ( state = = Qt : : WindowMinimized ) {
menuToDisable = SC_MINIMIZE ;
}
int itemCount = GetMenuItemCount ( ps_menu ) ;
for ( int i = 0 ; i < itemCount ; + + i ) {
MENUITEMINFO itemInfo = { 0 } ;
itemInfo . cbSize = sizeof ( itemInfo ) ;
itemInfo . fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID ;
if ( GetMenuItemInfo ( ps_menu , i , TRUE , & itemInfo ) ) {
if ( itemInfo . fType & MFT_SEPARATOR ) {
continue ;
}
if ( itemInfo . wID & & ! ( itemInfo . fState & MFS_DEFAULT ) ) {
UINT fOldState = itemInfo . fState , fState = itemInfo . fState & ~ MFS_DISABLED ;
if ( itemInfo . wID = = SC_CLOSE ) {
fState | = MFS_DEFAULT ;
} else if ( itemInfo . wID = = menuToDisable | | ( itemInfo . wID ! = SC_MINIMIZE & & itemInfo . wID ! = SC_MAXIMIZE & & itemInfo . wID ! = SC_RESTORE ) ) {
fState | = MFS_DISABLED ;
}
itemInfo . fMask = MIIM_STATE ;
itemInfo . fState = fState ;
if ( ! SetMenuItemInfo ( ps_menu , i , TRUE , & itemInfo ) ) {
DEBUG_LOG ( ( " PS Error: could not set state %1 to menu item %2, old state %3, error %4 " ) . arg ( fState ) . arg ( itemInfo . wID ) . arg ( fOldState ) . arg ( GetLastError ( ) ) ) ;
DestroyMenu ( ps_menu ) ;
ps_menu = 0 ;
break ;
}
}
} else {
DEBUG_LOG ( ( " PS Error: could not get state, menu item %1 of %2, error %3 " ) . arg ( i ) . arg ( itemCount ) . arg ( GetLastError ( ) ) ) ;
DestroyMenu ( ps_menu ) ;
ps_menu = 0 ;
break ;
}
}
}
2020-07-07 13:54:39 +00:00
void MainWindow : : updateCustomMargins ( ) {
if ( ! ps_hWnd | | _inUpdateMargins ) {
return ;
}
2018-03-30 15:36:31 +00:00
_inUpdateMargins = true ;
2016-06-16 12:59:54 +00:00
2020-07-07 13:54:39 +00:00
const auto margins = computeCustomMargins ( ) ;
if ( const auto native = QGuiApplication : : platformNativeInterface ( ) ) {
native - > setWindowProperty (
windowHandle ( ) - > handle ( ) ,
qsl ( " WindowsCustomMargins " ) ,
QVariant : : fromValue < QMargins > ( margins ) ) ;
}
if ( ! _themeInited ) {
_themeInited = true ;
2020-07-24 12:15:32 +00:00
validateWindowTheme (
Core : : App ( ) . settings ( ) . nativeWindowFrame ( ) ,
Window : : Theme : : IsNightMode ( ) ) ;
2020-07-07 13:54:39 +00:00
}
_inUpdateMargins = false ;
}
2016-06-16 12:59:54 +00:00
2020-07-07 13:54:39 +00:00
QMargins MainWindow : : computeCustomMargins ( ) {
if ( Core : : App ( ) . settings ( ) . nativeWindowFrame ( ) ) {
_deltaLeft = _deltaTop = _deltaRight = _deltaBottom = 0 ;
return QMargins ( ) ;
}
auto r = RECT ( ) ;
2016-06-16 12:59:54 +00:00
GetClientRect ( ps_hWnd , & r ) ;
2020-07-07 13:54:39 +00:00
auto a = r ;
const auto style = GetWindowLongPtr ( ps_hWnd , GWL_STYLE ) ;
const auto styleEx = GetWindowLongPtr ( ps_hWnd , GWL_EXSTYLE ) ;
2016-06-16 12:59:54 +00:00
AdjustWindowRectEx ( & a , style , false , styleEx ) ;
2020-07-07 13:54:39 +00:00
auto margins = QMargins ( a . left - r . left , a . top - r . top , r . right - a . right , r . bottom - a . bottom ) ;
2016-06-16 12:59:54 +00:00
if ( style & WS_MAXIMIZE ) {
RECT w , m ;
GetWindowRect ( ps_hWnd , & w ) ;
m = w ;
HMONITOR hMonitor = MonitorFromRect ( & w , MONITOR_DEFAULTTONEAREST ) ;
if ( hMonitor ) {
MONITORINFO mi ;
mi . cbSize = sizeof ( mi ) ;
GetMonitorInfo ( hMonitor , & mi ) ;
m = mi . rcWork ;
}
_deltaLeft = w . left - m . left ;
_deltaTop = w . top - m . top ;
2018-03-30 15:36:31 +00:00
_deltaRight = m . right - w . right ;
_deltaBottom = m . bottom - w . bottom ;
margins . setLeft ( margins . left ( ) - _deltaLeft ) ;
margins . setRight ( margins . right ( ) - _deltaRight ) ;
margins . setBottom ( margins . bottom ( ) - _deltaBottom ) ;
margins . setTop ( margins . top ( ) - _deltaTop ) ;
} else if ( _deltaLeft ! = 0 | | _deltaTop ! = 0 | | _deltaRight ! = 0 | | _deltaBottom ! = 0 ) {
RECT w ;
GetWindowRect ( ps_hWnd , & w ) ;
SetWindowPos ( ps_hWnd , 0 , 0 , 0 , w . right - w . left - _deltaLeft - _deltaRight , w . bottom - w . top - _deltaBottom - _deltaTop , SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION ) ;
_deltaLeft = _deltaTop = _deltaRight = _deltaBottom = 0 ;
2016-06-16 12:59:54 +00:00
}
2020-07-07 13:54:39 +00:00
return margins ;
}
2016-06-16 12:59:54 +00:00
2020-07-24 12:15:32 +00:00
void MainWindow : : validateWindowTheme ( bool native , bool night ) {
if ( ! Dlls : : SetWindowTheme ) {
return ;
} else if ( ! IsWindows8OrGreater ( ) ) {
const auto empty = native ? nullptr : L " " ;
Dlls : : SetWindowTheme ( ps_hWnd , empty , empty ) ;
QApplication : : setStyle ( QStyleFactory : : create ( u " Windows " _q ) ) ;
} else if ( ! Platform : : IsDarkModeSupported ( ) /*
| | ( ! Dlls : : AllowDarkModeForApp & & ! Dlls : : SetPreferredAppMode )
| | ! Dlls : : AllowDarkModeForWindow
| | ! Dlls : : RefreshImmersiveColorPolicyState
| | ! Dlls : : FlushMenuThemes */ ) {
return ;
} else if ( ! native ) {
Dlls : : SetWindowTheme ( ps_hWnd , nullptr , nullptr ) ;
2020-07-07 13:54:39 +00:00
return ;
2019-03-21 18:21:23 +00:00
}
2020-07-24 12:15:32 +00:00
// See "https://github.com/microsoft/terminal/blob/"
// "eb480b6bbbd83a2aafbe62992d360838e0ab9da5/"
// "src/interactivity/win32/windowtheme.cpp#L43-L63"
auto darkValue = BOOL ( night ? TRUE : FALSE ) ;
const auto updateStyle = [ & ] {
static const auto kSystemVersion = QOperatingSystemVersion : : current ( ) ;
if ( kSystemVersion . microVersion ( ) < 18362 ) {
SetPropW (
ps_hWnd ,
L " UseImmersiveDarkModeColors " ,
reinterpret_cast < HANDLE > ( static_cast < INT_PTR > ( darkValue ) ) ) ;
} else if ( Dlls : : SetWindowCompositionAttribute ) {
Dlls : : WINDOWCOMPOSITIONATTRIBDATA data = {
Dlls : : WINDOWCOMPOSITIONATTRIB : : WCA_USEDARKMODECOLORS ,
& darkValue ,
sizeof ( darkValue )
} ;
Dlls : : SetWindowCompositionAttribute ( ps_hWnd , & data ) ;
} else if ( Dlls : : DwmSetWindowAttribute ) {
static constexpr auto DWMWA_USE_IMMERSIVE_DARK_MODE_0 = DWORD ( 19 ) ;
static constexpr auto DWMWA_USE_IMMERSIVE_DARK_MODE = DWORD ( 20 ) ;
const auto set = [ & ] ( DWORD attribute ) {
return Dlls : : DwmSetWindowAttribute (
ps_hWnd ,
attribute ,
& darkValue ,
sizeof ( darkValue ) ) ;
} ;
if ( FAILED ( set ( DWMWA_USE_IMMERSIVE_DARK_MODE ) ) ) {
set ( DWMWA_USE_IMMERSIVE_DARK_MODE_0 ) ;
}
}
} ;
updateStyle ( ) ;
// See "https://osdn.net/projects/tortoisesvn/scm/svn/blobs/28812/"
// "trunk/src/TortoiseIDiff/MainWindow.cpp"
//
// But for now it works event with a small part of that.
//
//const auto updateWindowTheme = [&] {
// const auto set = [&](LPCWSTR name) {
// return Dlls::SetWindowTheme(ps_hWnd, name, nullptr);
// };
// if (!night || FAILED(set(L"DarkMode_Explorer"))) {
// set(L"Explorer");
// }
//};
//
//if (night) {
// if (Dlls::SetPreferredAppMode) {
// Dlls::SetPreferredAppMode(Dlls::PreferredAppMode::AllowDark);
// } else {
// Dlls::AllowDarkModeForApp(TRUE);
// }
// Dlls::AllowDarkModeForWindow(ps_hWnd, TRUE);
// updateWindowTheme();
// updateStyle();
// Dlls::FlushMenuThemes();
// Dlls::RefreshImmersiveColorPolicyState();
//} else {
// updateWindowTheme();
// Dlls::AllowDarkModeForWindow(ps_hWnd, FALSE);
// updateStyle();
// Dlls::FlushMenuThemes();
// Dlls::RefreshImmersiveColorPolicyState();
// if (Dlls::SetPreferredAppMode) {
// Dlls::SetPreferredAppMode(Dlls::PreferredAppMode::Default);
// } else {
// Dlls::AllowDarkModeForApp(FALSE);
// }
//}
// Didn't find any other way to definitely repaint with the new style.
SendMessage ( ps_hWnd , WM_NCACTIVATE , _hasActiveFrame ? 0 : 1 , 0 ) ;
SendMessage ( ps_hWnd , WM_NCACTIVATE , _hasActiveFrame ? 1 : 0 , 0 ) ;
2020-07-07 13:54:39 +00:00
}
void MainWindow : : fixMaximizedWindow ( ) {
auto r = RECT ( ) ;
GetClientRect ( ps_hWnd , & r ) ;
const auto style = GetWindowLongPtr ( ps_hWnd , GWL_STYLE ) ;
const auto styleEx = GetWindowLongPtr ( ps_hWnd , GWL_EXSTYLE ) ;
AdjustWindowRectEx ( & r , style , false , styleEx ) ;
if ( style & WS_MAXIMIZE ) {
auto w = RECT ( ) ;
GetWindowRect ( ps_hWnd , & w ) ;
if ( const auto hMonitor = MonitorFromRect ( & w , MONITOR_DEFAULTTONEAREST ) ) {
MONITORINFO mi ;
mi . cbSize = sizeof ( mi ) ;
GetMonitorInfo ( hMonitor , & mi ) ;
const auto m = mi . rcWork ;
SetWindowPos ( ps_hWnd , 0 , 0 , 0 , m . right - m . left - _deltaLeft - _deltaRight , m . bottom - m . top - _deltaTop - _deltaBottom , SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION ) ;
2016-06-16 12:59:54 +00:00
}
}
}
2021-01-22 12:16:18 +00:00
void MainWindow : : showFromTrayMenu ( ) {
// If we try to activate() window before the trayIconMenu is hidden,
// then the window will be shown in semi-active state (Qt bug).
// It will receive input events, but it will be rendered as inactive.
using namespace rpl : : mappers ;
_showFromTrayLifetime = trayIconMenu - > shownValue (
) | rpl : : filter ( _1 ) | rpl : : take ( 1 ) | rpl : : start_with_next ( [ = ] {
showFromTray ( ) ;
} ) ;
}
2016-06-16 12:59:54 +00:00
HWND MainWindow : : psHwnd ( ) const {
return ps_hWnd ;
}
HMENU MainWindow : : psMenu ( ) const {
return ps_menu ;
}
void MainWindow : : psDestroyIcons ( ) {
if ( ps_iconBig ) {
DestroyIcon ( ps_iconBig ) ;
ps_iconBig = 0 ;
}
if ( ps_iconSmall ) {
DestroyIcon ( ps_iconSmall ) ;
ps_iconSmall = 0 ;
}
if ( ps_iconOverlay ) {
DestroyIcon ( ps_iconOverlay ) ;
ps_iconOverlay = 0 ;
}
}
MainWindow : : ~ MainWindow ( ) {
if ( handleSessionNotification ) {
2019-03-21 18:21:23 +00:00
Dlls : : WTSUnRegisterSessionNotification ( ps_hWnd ) ;
}
2021-01-16 12:33:09 +00:00
_private - > viewSettings . Reset ( ) ;
2019-03-21 18:21:23 +00:00
if ( taskbarList ) {
taskbarList . Reset ( ) ;
2016-06-16 12:59:54 +00:00
}
if ( ps_menu ) DestroyMenu ( ps_menu ) ;
psDestroyIcons ( ) ;
if ( ps_tbHider_hWnd ) DestroyWindow ( ps_tbHider_hWnd ) ;
2019-06-07 11:30:30 +00:00
EventFilter : : Destroy ( ) ;
2016-06-16 12:59:54 +00:00
}
} // namespace Platform