Files
desktop-apps/win-linux/src/windows/platform_win/cwindowplatform.cpp
2024-02-26 14:25:42 +02:00

439 lines
15 KiB
C++

/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "windows/platform_win/cwindowplatform.h"
#include "cascapplicationmanagerwrapper.h"
#include "defines.h"
#include "utils.h"
#include <QTimer>
#include <QDesktopWidget>
#include <QWindow>
#include <QScreen>
#include <QJsonObject>
#include <shellapi.h>
//#define UM_SNAPPING 0x02
#define SKIP_EVENTS_QUEUE(callback) QTimer::singleShot(0, this, callback)
static bool isTaskbarAutoHideOn()
{
APPBARDATA ABData;
ABData.cbSize = sizeof(ABData);
return (SHAppBarMessage(ABM_GETSTATE, &ABData) & ABS_AUTOHIDE) != 0;
}
CWindowPlatform::CWindowPlatform(const QRect &rect) :
CWindowBase(rect),
m_hWnd(nullptr),
m_resAreaWidth(MAIN_WINDOW_BORDER_WIDTH),
m_borderless(true),
m_closed(false),
m_isResizeable(true)
// m_allowMaximize(true)
{
if (AscAppManager::isRtlEnabled())
setLayoutDirection(Qt::RightToLeft);
setWindowFlags(windowFlags() | Qt::Window | Qt::FramelessWindowHint
| Qt::WindowSystemMenuHint | Qt::WindowMaximizeButtonHint
|Qt::WindowMinimizeButtonHint | Qt::MSWindowsFixedSizeDialogHint);
m_borderless = isCustomWindowStyle();
m_hWnd = (HWND)winId();
LONG style = ::GetWindowLong(m_hWnd, GWL_STYLE);
style &= ~(WS_CAPTION | WS_SYSMENU | WS_THICKFRAME);
style |= (WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
style |= (Utils::getWinVersion() > Utils::WinVer::Win7) ? WS_OVERLAPPEDWINDOW : WS_POPUP;
::SetWindowLong(m_hWnd, GWL_STYLE, style);
connect(this->window()->windowHandle(), &QWindow::screenChanged, this, [=]() {
SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
});
setProperty("stabilized", true);
m_propertyTimer = new QTimer(this);
m_propertyTimer->setSingleShot(true);
m_propertyTimer->setInterval(100);
connect(m_propertyTimer, &QTimer::timeout, this, [=]() {
setProperty("stabilized", true);
});
}
CWindowPlatform::~CWindowPlatform()
{
m_closed = true;
}
/** Public **/
//void CWindowPlatform::toggleBorderless(bool showmax)
//{
// if (isVisible()) {
// m_borderless = !m_borderless;
// show(showmax);
// }
//}
//void CWindowPlatform::toggleResizeable()
//{
// m_isResizeable = !m_isResizeable;
//}
void CWindowPlatform::bringToTop()
{
if (IsIconic(m_hWnd)) {
ShowWindow(m_hWnd, SW_RESTORE);
}
WindowHelper::bringToTop(m_hWnd);
}
void CWindowPlatform::show(bool maximized)
{
maximized ? CWindowBase::showMaximized() : CWindowBase::show();
}
void CWindowPlatform::adjustGeometry()
{
if (windowState().testFlag(Qt::WindowMinimized) || windowState().testFlag(Qt::WindowNoState)) {
const int border = int(MAIN_WINDOW_BORDER_WIDTH * m_dpiRatio);
setContentsMargins(border, border, border, border+1);
m_resAreaWidth = border;
} else
if (windowState().testFlag(Qt::WindowMaximized)) {
QTimer::singleShot(25, this, [=]() {
auto rc = QApplication::desktop()->availableGeometry(this);
const QSize offset(0, !isTaskbarAutoHideOn() ? 0 : 2);
SetWindowPos(m_hWnd, NULL, rc.x(), rc.y(), rc.width(), rc.height() - offset.height(),
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
#ifdef __OS_WIN_XP
setContentsMargins(0, 0, 0, 0);
#else
int border = 0;
if (!isTaskbarAutoHideOn() && Utils::getWinVersion() > Utils::WinVer::Win7) {
double dpi = qApp->screenAt(geometry().center())->logicalDotsPerInch()/96;
border = (dpi <= 1.0) ? 8 :
(dpi == 1.25) ? 9 :
(dpi == 1.5) ? 11 :
(dpi == 1.75) ? 12 :
(dpi == 2.0) ? 13 :
(dpi == 2.25) ? 14 :
(dpi == 2.5) ? 16 : 6 * dpi;
}
setContentsMargins(border, border, border, border);
#endif
});
}
}
/** Protected **/
bool CWindowPlatform::isSessionInProgress()
{
return m_isSessionInProgress;
}
bool CWindowPlatform::event(QEvent * event)
{
if (event->type() == QEvent::LayoutDirectionChange) {
if (m_pMainPanel) {
m_pMainPanel->setProperty("rtl", AscAppManager::isRtlEnabled());
onLayoutDirectionChanged();
}
}
return CWindowBase::event(event);
}
/** Private **/
void CWindowPlatform::changeEvent(QEvent *event)
{
CWindowBase::changeEvent(event);
if (event->type() == QEvent::WindowStateChange) {
applyWindowState();
adjustGeometry();
}
}
bool CWindowPlatform::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
MSG* msg = *reinterpret_cast<MSG**>(message);
#else
MSG* msg = reinterpret_cast<MSG*>(message);
#endif
// static uchar movParam = 0;
switch (msg->message)
{
case WM_ACTIVATE: {
#ifndef __OS_WIN_XP
MARGINS mrg = {4, 4, 29, 4};
if (Utils::getWinVersion() > Utils::WinVer::Win10) {
mrg.cxLeftWidth = 1;
mrg.cxRightWidth = 0;
mrg.cyBottomHeight = 0;
mrg.cyTopHeight = 0;
}
DwmExtendFrameIntoClientArea(m_hWnd, &mrg);
#endif
break;
}
case WM_DPICHANGED: {
setMinimumSize(0,0);
if (AscAppManager::IsUseSystemScaling()) {
if (WindowHelper::isLeftButtonPressed() || (m_scaleChanged && !isMaximized())) {
RECT *prefRect = (RECT*)msg->lParam;
setGeometry(prefRect->left, prefRect->top, prefRect->right - prefRect->left, prefRect->bottom - prefRect->top);
}
SKIP_EVENTS_QUEUE([=]() {
updateScaling(false);
});
} else
if (m_scaleChanged && !isMaximized()) {
RECT *prefRect = (RECT*)msg->lParam;
setGeometry(prefRect->left, prefRect->top, prefRect->right - prefRect->left, prefRect->bottom - prefRect->top);
}
m_scaleChanged = false;
break;
}
case WM_DISPLAYCHANGE: {
m_scaleChanged = true;
break;
}
// case WM_SYSKEYDOWN: {
// if (msg->wParam == VK_SPACE) {
// RECT winrect;
// GetWindowRect(msg->hwnd, &winrect);
// TrackPopupMenu(GetSystemMenu(msg->hwnd, false ), TPM_TOPALIGN | TPM_LEFTALIGN, winrect.left + 5, winrect.top + 5, 0, msg->hwnd, NULL);
// }
// break;
// }
case WM_NCCALCSIZE: {
if (!m_borderless || !msg->wParam)
break;
NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS*)msg->lParam;
params->rgrc[0].bottom += 1;
*result = WVR_ALIGNLEFT | WVR_ALIGNTOP | WVR_REDRAW;
return true;
}
case WM_NCHITTEST: {
if (m_borderless && m_isResizeable) {
*result = 0;
RECT rect;
GetWindowRect(msg->hwnd, &rect);
int x = GET_X_LPARAM(msg->lParam);
int y = GET_Y_LPARAM(msg->lParam);
if (x <= rect.left + m_resAreaWidth) {
if (y <= rect.top + m_resAreaWidth)
*result = HTTOPLEFT;
else
if (y > rect.top + m_resAreaWidth && y < rect.bottom - m_resAreaWidth)
*result = HTLEFT;
else
if (y >= rect.bottom - m_resAreaWidth)
*result = HTBOTTOMLEFT;
} else
if (x > rect.left + m_resAreaWidth && x < rect.right - m_resAreaWidth) {
if (y <= rect.top + m_resAreaWidth)
*result = HTTOP;
else
if (y >= rect.bottom - m_resAreaWidth)
*result = HTBOTTOM;
} else
if (x >= rect.right - m_resAreaWidth) {
if (y <= rect.top + m_resAreaWidth)
*result = HTTOPRIGHT;
else
if (y > rect.top + m_resAreaWidth && y < rect.bottom - m_resAreaWidth)
*result = HTRIGHT;
else
if (y >= rect.bottom - m_resAreaWidth)
*result = HTBOTTOMRIGHT;
}
return (*result != 0);
}
break;
}
case WM_SETFOCUS: {
if (!m_closed && IsWindowEnabled(m_hWnd)) {
SKIP_EVENTS_QUEUE([=]() {
focus();
});
}
m_propertyTimer->stop();
if (property("stabilized").toBool())
setProperty("stabilized", false);
m_propertyTimer->start();
break;
}
case WM_SETTINGCHANGE: {
if (msg->wParam == SPI_SETWINARRANGING) {
if (Utils::getWinVersion() > Utils::WinVer::Win10 && m_boxTitleBtns)
SendMessage((HWND)m_boxTitleBtns->winId(), WM_SETTINGCHANGE, 0, 0);
} else
if (msg->wParam == SPI_SETWORKAREA) {
static RECT oldWorkArea = {0,0,0,0};
RECT workArea; // Taskbar show/hide detection
SystemParametersInfoW(SPI_GETWORKAREA, 0, &workArea, 0);
if (!EqualRect(&oldWorkArea, &workArea)) {
oldWorkArea = workArea;
QTimer::singleShot(200, this, [=]() {
adjustGeometry();
});
}
} else if (msg->wParam == 0) {
const std::wstring param{(wchar_t*)msg->lParam};
if (param == L"ImmersiveColorSet") {
QSettings _reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings::NativeFormat);
bool _changed_theme_dark = _reg.value("AppsUseLightTheme", 1).toInt() == 0;
if (_changed_theme_dark != AscAppManager::themes().isSystemSchemeDark()) {
NSEditorApi::CAscCefMenuEvent * ns_event = new NSEditorApi::CAscCefMenuEvent(ASC_MENU_EVENT_TYPE_CEF_EXECUTE_COMMAND);
NSEditorApi::CAscExecCommand * pData = new NSEditorApi::CAscExecCommand;
pData->put_Command(L"system:changed");
QJsonObject _json_obj{{"colorscheme", _changed_theme_dark ? "dark" : "light"}};
pData->put_Param(Utils::stringifyJson(_json_obj).toStdWString());
ns_event->m_pData = pData;
ns_event->m_nSenderId = 0;
AscAppManager::getInstance().OnEvent(ns_event);
}
}
}
break;
}
case WM_TIMER:
AscAppManager::getInstance().CheckKeyboard();
break;
case UM_INSTALL_UPDATE:
QTimer::singleShot(500, this, [=](){
onCloseEvent();
});
break;
case WM_POWERBROADCAST: {
if (msg->wParam == PBT_APMRESUMEAUTOMATIC) {
HMONITOR monitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONULL);
if (!monitor) {
moveToPrimaryScreen();
} else {
MONITORINFOEX monInfo;
ZeroMemory(&monInfo, sizeof(monInfo));
monInfo.cbSize = sizeof(MONITORINFOEX);
if (GetMonitorInfo(monitor, &monInfo)) {
DISPLAY_DEVICE dispDevice;
ZeroMemory(&dispDevice, sizeof(dispDevice));
dispDevice.cb = sizeof(dispDevice);
if (EnumDisplayDevices(monInfo.szDevice, 0, &dispDevice, EDD_GET_DEVICE_INTERFACE_NAME)) {
HANDLE hDevice;
hDevice = CreateFile(dispDevice.DeviceID, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if (hDevice != INVALID_HANDLE_VALUE) {
BOOL res;
if (GetDevicePowerState(hDevice, &res)) {
if (res == FALSE)
moveToPrimaryScreen();
} else {
moveToPrimaryScreen();
}
CloseHandle(hDevice);
} else {
moveToPrimaryScreen();
}
}
} else {
moveToPrimaryScreen();
}
}
}
break;
}
// case WM_EXITSIZEMOVE:
// if (m_allowMaximize)
// QApplication::postEvent(this, new QEvent(QEvent::User));
// break;
// case WM_MOVE:
// if (movParam != 0)
// movParam = 0;
// break;
// case WM_MOVING:
// if (m_allowMaximize && ++movParam == UM_SNAPPING)
// m_allowMaximize = false;
// break;
case WM_PAINT:
return false;
case WM_ERASEBKGND:
return true;
case WM_NCACTIVATE: {
// Prevent the title bar from being drawn when the window is restored or maximized
if (m_borderless) {
if (!LOWORD(msg->wParam)) {
*result = TRUE;
break;
}
return true;
}
break;
}
case WM_QUERYENDSESSION:
Utils::setSessionInProgress(false);
m_isSessionInProgress = false;
break;
case WM_ENDSESSION:
if (!msg->wParam) {
Utils::setSessionInProgress(true);
m_isSessionInProgress = true;
}
break;
default:
break;
}
return CWindowBase::nativeEvent(eventType, message, result);
}