Fix Windows 11 title controls by additional padding.

This commit is contained in:
John Preston 2022-01-17 13:37:14 +03:00
parent 9b5c939fd0
commit 220d683528
3 changed files with 182 additions and 10 deletions

View file

@ -6,9 +6,13 @@
//
#include "ui/platform/win/ui_window_title_win.h"
#include "ui/platform/win/ui_window_win.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/ui_utility.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/win/base_windows_safe_library.h"
#include "base/debug_log.h"
#include "styles/style_widgets.h"
#include "styles/palette.h"
@ -16,28 +20,75 @@
#include <QtGui/QtEvents>
#include <QtGui/QWindow>
#include <windows.h>
#include <shellscalingapi.h>
namespace Ui {
namespace Platform {
namespace {
HRESULT(__stdcall *GetScaleFactorForMonitor)(
_In_ HMONITOR hMon,
_Out_ DEVICE_SCALE_FACTOR *pScale);
[[nodiscard]] bool ScaleQuerySupported() {
static const auto Result = [&] {
#define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name)
const auto shcore = base::Platform::SafeLoadLibrary(L"Shcore.dll");
return LOAD_SYMBOL(shcore, GetScaleFactorForMonitor);
#undef LOAD_SYMBOL
}();
return Result;
}
} // namespace
struct TitleWidget::PaddingHelper {
explicit PaddingHelper(QWidget *parent) : controlsParent(parent) {
}
RpWidget controlsParent;
int padding = 0;
};
TitleWidget::TitleWidget(not_null<RpWidget*> parent)
: RpWidget(parent)
, _controls(this, st::defaultWindowTitle)
, _paddingHelper(CheckTitlePaddingRequired()
? std::make_unique<PaddingHelper>(this)
: nullptr)
, _controls(
_paddingHelper ? &_paddingHelper->controlsParent : this,
st::defaultWindowTitle)
, _shadow(this, st::titleShadow) {
setAttribute(Qt::WA_OpaquePaintEvent);
parent->widthValue(
) | rpl::start_with_next([=](int width) {
setGeometry(0, 0, width, _controls.st()->height);
refreshGeometryWithWidth(width);
}, lifetime());
}
TitleWidget::~TitleWidget() = default;
void TitleWidget::setText(const QString &text) {
window()->setWindowTitle(text);
}
void TitleWidget::setStyle(const style::WindowTitle &st) {
_controls.setStyle(st);
setGeometry(0, 0, window()->width(), _controls.st()->height);
refreshGeometryWithWidth(window()->width());
}
void TitleWidget::refreshGeometryWithWidth(int width) {
const auto add = _paddingHelper ? _paddingHelper->padding : 0;
setGeometry(0, 0, width, _controls.st()->height + add);
if (_paddingHelper) {
_paddingHelper->controlsParent.setGeometry(
add,
add,
width - 2 * add,
_controls.st()->height);
}
update();
}
@ -57,7 +108,8 @@ void TitleWidget::paintEvent(QPaintEvent *e) {
}
void TitleWidget::resizeEvent(QResizeEvent *e) {
_shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth);
const auto thickness = st::lineWidth;
_shadow->setGeometry(0, height() - thickness, width(), thickness);
}
HitTestResult TitleWidget::hitTest(QPoint point) const {
@ -69,5 +121,99 @@ HitTestResult TitleWidget::hitTest(QPoint point) const {
return HitTestResult::None;
}
bool TitleWidget::additionalPaddingRequired() const {
return _paddingHelper && !isHidden();
}
void TitleWidget::refreshAdditionalPaddings() {
if (!additionalPaddingRequired()) {
return;
}
const auto handle = GetWindowHandle(this);
if (!handle) {
LOG(("System Error: GetWindowHandle failed."));
return;
}
refreshAdditionalPaddings(handle);
}
void TitleWidget::refreshAdditionalPaddings(HWND handle) {
if (!additionalPaddingRequired()) {
return;
}
auto placement = WINDOWPLACEMENT{
.length = sizeof(WINDOWPLACEMENT),
};
if (!GetWindowPlacement(handle, &placement)) {
LOG(("System Error: GetWindowPlacement failed."));
return;
}
refreshAdditionalPaddings(handle, placement);
}
void TitleWidget::refreshAdditionalPaddings(
HWND handle,
const WINDOWPLACEMENT &placement) {
auto geometry = RECT();
if (!GetWindowRect(handle, &geometry)) {
LOG(("System Error: GetWindowRect failed."));
return;
}
const auto normal = placement.rcNormalPosition;
const auto rounded = (normal.left == geometry.left)
&& (normal.right == geometry.right)
&& (normal.top == geometry.top)
&& (normal.bottom == geometry.bottom);
const auto padding = [&] {
if (!rounded) {
return 0;
}
const auto monitor = MonitorFromWindow(
handle,
MONITOR_DEFAULTTONEAREST);
if (!monitor) {
LOG(("System Error: MonitorFromWindow failed."));
return -1;
}
auto factor = DEVICE_SCALE_FACTOR();
if (!SUCCEEDED(GetScaleFactorForMonitor(monitor, &factor))) {
LOG(("System Error: GetScaleFactorForMonitor failed."));
return -1;
} else if (factor < 100 || factor > 500) {
LOG(("System Error: Bad scale factor %1.").arg(int(factor)));
return -1;
}
const auto pixels = (factor + 50) / 100;
return int(base::SafeRound(pixels / window()->devicePixelRatioF()));
}();
if (padding < 0) {
return;
}
setAdditionalPadding(padding);
}
void TitleWidget::setAdditionalPadding(int padding) {
Expects(_paddingHelper != nullptr);
if (_paddingHelper->padding == padding) {
return;
}
_paddingHelper->padding = padding;
refreshGeometryWithWidth(window()->width());
}
void TitleWidget::setVisibleHook(bool visible) {
RpWidget::setVisibleHook(visible);
if (additionalPaddingRequired()) {
PostponeCall(this, [=] {
refreshAdditionalPaddings();
});
}
}
bool CheckTitlePaddingRequired() {
return ::Platform::IsWindows11OrGreater() && ScaleQuerySupported();
}
} // namespace Platform
} // namespace Ui

View file

@ -13,6 +13,8 @@
#include <QtCore/QRect>
#include <QtCore/QPoint>
#include <Windows.h> // HWND, WINDOWPLACEMENT
namespace style {
struct WindowTitle;
} // namespace style
@ -42,6 +44,7 @@ enum class HitTestResult {
class TitleWidget : public RpWidget {
public:
explicit TitleWidget(not_null<RpWidget*> parent);
~TitleWidget();
void setText(const QString &text);
void setStyle(const style::WindowTitle &st);
@ -49,15 +52,32 @@ public:
[[nodiscard]] HitTestResult hitTest(QPoint point) const;
void setResizeEnabled(bool enabled);
void refreshAdditionalPaddings();
void refreshAdditionalPaddings(HWND handle);
void refreshAdditionalPaddings(
HWND handle,
const WINDOWPLACEMENT &placement);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void setVisibleHook(bool visible) override;
private:
struct PaddingHelper;
[[nodiscard]] bool additionalPaddingRequired() const;
void refreshGeometryWithWidth(int width);
void setAdditionalPadding(int padding);
std::unique_ptr<PaddingHelper> _paddingHelper;
TitleControls _controls;
object_ptr<Ui::PlainShadow> _shadow;
};
[[nodiscard]] bool CheckTitlePaddingRequired();
} // namespace Platform
} // namespace Ui

View file

@ -9,7 +9,6 @@
#include "ui/inactive_press.h"
#include "ui/platform/win/ui_window_title_win.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/win/base_windows_safe_library.h"
#include "base/integration.h"
#include "base/debug_log.h"
#include "styles/palette.h"
@ -409,11 +408,16 @@ bool WindowHelper::handleNativeEvent(
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED: {
if (_shadow) {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(_handle, &wp)
&& (wp.showCmd == SW_SHOWMAXIMIZED
|| wp.showCmd == SW_SHOWMINIMIZED)) {
auto placement = WINDOWPLACEMENT{
.length = sizeof(WINDOWPLACEMENT),
};
if (!GetWindowPlacement(_handle, &placement)) {
LOG(("System Error: GetWindowPlacement failed."));
return false;
}
_title->refreshAdditionalPaddings(_handle, placement);
if (placement.showCmd == SW_SHOWMAXIMIZED
|| placement.showCmd == SW_SHOWMINIMIZED) {
_shadow->update(WindowShadow::Change::Hidden);
} else {
_shadow->update(
@ -439,6 +443,7 @@ bool WindowHelper::handleNativeEvent(
}
updateMargins();
if (_shadow) {
_title->refreshAdditionalPaddings(_handle);
const auto changes = (wParam == SIZE_MINIMIZED
|| wParam == SIZE_MAXIMIZED)
? WindowShadow::Change::Hidden
@ -462,6 +467,7 @@ bool WindowHelper::handleNativeEvent(
case WM_MOVE: {
if (_shadow) {
_title->refreshAdditionalPaddings(_handle);
_shadow->update(WindowShadow::Change::Moved);
}
} return false;