Add IsOverlapped method

This commit is contained in:
Ilya Fedin 2021-04-10 14:56:33 +04:00 committed by John Preston
parent a37e28d2f3
commit 50a0e7da64
6 changed files with 367 additions and 1 deletions

View file

@ -33,6 +33,259 @@ namespace {
constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs;
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
std::optional<bool> XCBWindowMapped(xcb_window_t window) {
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return std::nullopt;
}
const auto cookie = xcb_get_window_attributes(connection, window);
const auto reply = xcb_get_window_attributes_reply(
connection,
cookie,
nullptr);
if (!reply) {
return std::nullopt;
}
const auto guard = gsl::finally([&] { free(reply); });
return reply->map_state == XCB_MAP_STATE_VIEWABLE;
}
std::optional<bool> XCBWindowHidden(xcb_window_t window) {
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return std::nullopt;
}
const auto stateAtom = base::Platform::XCB::GetAtom(
connection,
"_NET_WM_STATE");
const auto stateHiddenAtom = base::Platform::XCB::GetAtom(
connection,
"_NET_WM_STATE_HIDDEN");
if (!stateAtom.has_value() || !stateHiddenAtom.has_value()) {
return std::nullopt;
}
const auto cookie = xcb_get_property(
connection,
false,
window,
*stateAtom,
XCB_ATOM_ATOM,
0,
1024);
const auto reply = xcb_get_property_reply(
connection,
cookie,
nullptr);
if (!reply) {
return std::nullopt;
}
const auto guard = gsl::finally([&] { free(reply); });
if (reply->type != XCB_ATOM_ATOM || reply->format != 32) {
return std::nullopt;
}
const auto atomsStart = reinterpret_cast<xcb_atom_t*>(
xcb_get_property_value(reply));
const auto states = std::vector<xcb_atom_t>(
atomsStart,
atomsStart + reply->length);
return ranges::contains(states, *stateHiddenAtom);
}
QRect XCBWindowGeometry(xcb_window_t window) {
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return {};
}
const auto cookie = xcb_get_geometry(connection, window);
const auto reply = xcb_get_geometry_reply(
connection,
cookie,
nullptr);
if (!reply) {
return {};
}
const auto guard = gsl::finally([&] { free(reply); });
return QRect(reply->x, reply->y, reply->width, reply->height);
}
std::optional<uint> XCBCurrentWorkspace() {
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return std::nullopt;
}
const auto root = base::Platform::XCB::GetRootWindowFromQt();
if (!root.has_value()) {
return std::nullopt;
}
const auto currentDesktopAtom = base::Platform::XCB::GetAtom(
connection,
"_NET_CURRENT_DESKTOP");
if (!currentDesktopAtom.has_value()) {
return std::nullopt;
}
const auto cookie = xcb_get_property(
connection,
false,
*root,
*currentDesktopAtom,
XCB_ATOM_CARDINAL,
0,
1024);
const auto reply = xcb_get_property_reply(
connection,
cookie,
nullptr);
if (!reply) {
return std::nullopt;
}
const auto guard = gsl::finally([&] { free(reply); });
return (reply->type == XCB_ATOM_CARDINAL)
? std::make_optional(
*reinterpret_cast<ulong*>(xcb_get_property_value(reply)))
: std::nullopt;
}
std::optional<uint> XCBWindowWorkspace(xcb_window_t window) {
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return std::nullopt;
}
const auto desktopAtom = base::Platform::XCB::GetAtom(
connection,
"_NET_WM_DESKTOP");
if (!desktopAtom.has_value()) {
return std::nullopt;
}
const auto cookie = xcb_get_property(
connection,
false,
window,
*desktopAtom,
XCB_ATOM_CARDINAL,
0,
1024);
const auto reply = xcb_get_property_reply(
connection,
cookie,
nullptr);
if (!reply) {
return std::nullopt;
}
const auto guard = gsl::finally([&] { free(reply); });
return (reply->type == XCB_ATOM_CARDINAL)
? std::make_optional(
*reinterpret_cast<ulong*>(xcb_get_property_value(reply)))
: std::nullopt;
}
std::optional<bool> XCBIsOverlapped(
not_null<QWidget*> widget,
const QRect &rect) {
const auto window = widget->window()->winId();
Expects(window != XCB_WINDOW_NONE);
const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) {
return std::nullopt;
}
const auto root = base::Platform::XCB::GetRootWindowFromQt();
if (!root.has_value()) {
return std::nullopt;
}
const auto windowWorkspace = XCBWindowWorkspace(
window);
const auto currentWorkspace = XCBCurrentWorkspace();
if (windowWorkspace.has_value()
&& currentWorkspace.has_value()
&& *windowWorkspace != *currentWorkspace
&& *windowWorkspace != 0xFFFFFFFF) {
return true;
}
const auto cookie = xcb_query_tree(connection, *root);
const auto reply = xcb_query_tree_reply(connection, cookie, nullptr);
if (!reply) {
return std::nullopt;
}
const auto guard = gsl::finally([&] { free(reply); });
const auto tree = xcb_query_tree_children(reply);
auto aboveTheWindow = false;
for (auto i = 0, l = xcb_query_tree_children_length(reply); i < l; ++i) {
if (window == tree[i]) {
aboveTheWindow = true;
continue;
}
if (!aboveTheWindow) {
continue;
}
const auto geometry = XCBWindowGeometry(tree[i]);
if (!rect.intersects(geometry)) {
continue;
}
const auto workspace = XCBWindowWorkspace(tree[i]);
if (workspace.has_value()
&& windowWorkspace.has_value()
&& *workspace != *windowWorkspace
&& *workspace != 0xFFFFFFFF) {
continue;
}
const auto mapped = XCBWindowMapped(tree[i]);
if (mapped.has_value() && !*mapped) {
continue;
}
const auto hidden = XCBWindowHidden(tree[i]);
if (hidden.has_value()
&& *hidden) {
continue;
}
return true;
}
return false;
}
bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) { bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) {
const auto connection = base::Platform::XCB::GetConnectionFromQt(); const auto connection = base::Platform::XCB::GetConnectionFromQt();
if (!connection) { if (!connection) {
@ -189,12 +442,24 @@ void ClearTransientParent(not_null<QWidget*> widget) {
if (::Platform::IsX11()) { if (::Platform::IsX11()) {
xcb_delete_property( xcb_delete_property(
base::Platform::XCB::GetConnectionFromQt(), base::Platform::XCB::GetConnectionFromQt(),
widget->windowHandle()->winId(), widget->winId(),
XCB_ATOM_WM_TRANSIENT_FOR); XCB_ATOM_WM_TRANSIENT_FOR);
} }
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
} }
std::optional<bool> IsOverlapped(
not_null<QWidget*> widget,
const QRect &rect) {
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
if (::Platform::IsX11()) {
return XCBIsOverlapped(widget, rect);
}
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
return std::nullopt;
}
bool WindowExtentsSupported() { bool WindowExtentsSupported() {
#ifdef DESKTOP_APP_QT_PATCHED #ifdef DESKTOP_APP_QT_PATCHED
if (::Platform::IsWayland()) { if (::Platform::IsWayland()) {

View file

@ -7,6 +7,7 @@
#include "ui/platform/mac/ui_utility_mac.h" #include "ui/platform/mac/ui_utility_mac.h"
#include "ui/integration.h" #include "ui/integration.h"
#include "base/qt_adapters.h"
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <QtGui/QtEvents> #include <QtGui/QtEvents>
@ -99,6 +100,12 @@ void DrainMainQueue() {
void IgnoreAllActivation(not_null<QWidget*> widget) { void IgnoreAllActivation(not_null<QWidget*> widget) {
} }
std::optional<bool> IsOverlapped(
not_null<QWidget*> widget,
const QRect &rect) {
return std::nullopt;
}
TitleControls::Layout TitleControlsLayout() { TitleControls::Layout TitleControlsLayout() {
return TitleControls::Layout{ return TitleControls::Layout{
.left = { .left = {

View file

@ -30,6 +30,10 @@ void BringToBack(not_null<QWidget*> widget);
void IgnoreAllActivation(not_null<QWidget*> widget); void IgnoreAllActivation(not_null<QWidget*> widget);
void ClearTransientParent(not_null<QWidget*> widget); void ClearTransientParent(not_null<QWidget*> widget);
[[nodiscard]] std::optional<bool> IsOverlapped(
not_null<QWidget*> widget,
const QRect &rect);
[[nodiscard]] constexpr bool UseMainQueueGeneric(); [[nodiscard]] constexpr bool UseMainQueueGeneric();
void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false. void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false.

View file

@ -11,6 +11,11 @@
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <wrl/client.h>
#include <Shobjidl.h>
using namespace Microsoft::WRL;
namespace Ui { namespace Ui {
namespace Platform { namespace Platform {
@ -45,6 +50,55 @@ void IgnoreAllActivation(not_null<QWidget*> widget) {
ShowWindow(handle, SW_SHOW); ShowWindow(handle, SW_SHOW);
} }
std::optional<bool> IsOverlapped(
not_null<QWidget*> widget,
const QRect &rect) {
const auto handle = reinterpret_cast<HWND>(widget->window()->winId());
Expects(handle != nullptr);
ComPtr<IVirtualDesktopManager> virtualDesktopManager;
HRESULT hr = CoCreateInstance(
CLSID_VirtualDesktopManager,
nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(&virtualDesktopManager));
if (SUCCEEDED(hr)) {
BOOL isCurrent;
hr = virtualDesktopManager->IsWindowOnCurrentVirtualDesktop(
handle,
&isCurrent);
if (SUCCEEDED(hr) && !isCurrent) {
return true;
}
}
std::vector<HWND> visited;
const RECT nativeRect{
rect.left(),
rect.top(),
rect.right(),
rect.bottom(),
};
for (auto curHandle = handle;
curHandle != nullptr && !ranges::contains(visited, curHandle);
curHandle = GetWindow(curHandle, GW_HWNDPREV)) {
visited.push_back(curHandle);
if (curHandle == handle) {
continue;
}
RECT testRect, intersection;
if (IsWindowVisible(curHandle)
&& GetWindowRect(curHandle, &testRect)
&& IntersectRect(&intersection, &nativeRect, &testRect)) {
return true;
}
}
return false;
}
bool ShowWindowMenu(QWindow *window) { bool ShowWindowMenu(QWindow *window) {
const auto pos = QCursor::pos(); const auto pos = QCursor::pos();

View file

@ -6,11 +6,13 @@
// //
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/style/style_core.h" #include "ui/style/style_core.h"
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <QtGui/QtEvents> #include <QtGui/QtEvents>
#include <private/qhighdpiscaling_p.h>
#include <array> #include <array>
@ -179,6 +181,36 @@ QPixmap PixmapFromImage(QImage &&image) {
return QPixmap::fromImage(std::move(image), Qt::ColorOnly); return QPixmap::fromImage(std::move(image), Qt::ColorOnly);
} }
bool IsContentVisible(
not_null<QWidget*> widget,
const QRect &rect) {
Expects(widget->window()->windowHandle());
const auto activeOrNotOverlapped = [&] {
if (const auto active = widget->isActiveWindow()) {
return active;
}
const auto mappedRect = QHighDpi::toNativePixels(
rect.isNull()
? QRect(
widget->mapToGlobal(QPoint()),
widget->mapToGlobal(
QPoint(widget->width(), widget->height())))
: QRect(
widget->mapToGlobal(rect.topLeft()),
widget->mapToGlobal(rect.bottomRight())),
widget->window()->windowHandle());
const auto overlapped = Platform::IsOverlapped(widget, mappedRect);
return overlapped.has_value() && !*overlapped;
}();
return activeOrNotOverlapped
&& widget->isVisible()
&& !widget->window()->isMinimized();
}
void DisableCustomScaling() { void DisableCustomScaling() {
qunsetenv("QT_DEVICE_PIXEL_RATIO"); qunsetenv("QT_DEVICE_PIXEL_RATIO");
qunsetenv("QT_SCALE_FACTOR"); qunsetenv("QT_SCALE_FACTOR");

View file

@ -187,6 +187,10 @@ QPointer<const Widget> MakeWeak(not_null<const Widget*> object) {
[[nodiscard]] QPixmap PixmapFromImage(QImage &&image); [[nodiscard]] QPixmap PixmapFromImage(QImage &&image);
[[nodiscard]] bool IsContentVisible(
not_null<QWidget*> widget,
const QRect &rect = QRect());
void DisableCustomScaling(); void DisableCustomScaling();
} // namespace Ui } // namespace Ui