Add IsOverlapped method
This commit is contained in:
parent
a37e28d2f3
commit
50a0e7da64
6 changed files with 367 additions and 1 deletions
|
|
@ -33,6 +33,259 @@ namespace {
|
|||
constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs;
|
||||
|
||||
#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) {
|
||||
const auto connection = base::Platform::XCB::GetConnectionFromQt();
|
||||
if (!connection) {
|
||||
|
|
@ -189,12 +442,24 @@ void ClearTransientParent(not_null<QWidget*> widget) {
|
|||
if (::Platform::IsX11()) {
|
||||
xcb_delete_property(
|
||||
base::Platform::XCB::GetConnectionFromQt(),
|
||||
widget->windowHandle()->winId(),
|
||||
widget->winId(),
|
||||
XCB_ATOM_WM_TRANSIENT_FOR);
|
||||
}
|
||||
#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() {
|
||||
#ifdef DESKTOP_APP_QT_PATCHED
|
||||
if (::Platform::IsWayland()) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "ui/platform/mac/ui_utility_mac.h"
|
||||
|
||||
#include "ui/integration.h"
|
||||
#include "base/qt_adapters.h"
|
||||
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QtEvents>
|
||||
|
|
@ -99,6 +100,12 @@ void DrainMainQueue() {
|
|||
void IgnoreAllActivation(not_null<QWidget*> widget) {
|
||||
}
|
||||
|
||||
std::optional<bool> IsOverlapped(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &rect) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TitleControls::Layout TitleControlsLayout() {
|
||||
return TitleControls::Layout{
|
||||
.left = {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ void BringToBack(not_null<QWidget*> widget);
|
|||
void IgnoreAllActivation(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();
|
||||
void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false.
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@
|
|||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <wrl/client.h>
|
||||
#include <Shobjidl.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace Ui {
|
||||
namespace Platform {
|
||||
|
||||
|
|
@ -45,6 +50,55 @@ void IgnoreAllActivation(not_null<QWidget*> widget) {
|
|||
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) {
|
||||
const auto pos = QCursor::pos();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@
|
|||
//
|
||||
#include "ui/ui_utility.h"
|
||||
|
||||
#include "ui/platform/ui_platform_utility.h"
|
||||
#include "ui/style/style_core.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QtEvents>
|
||||
#include <private/qhighdpiscaling_p.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
|
|
@ -179,6 +181,36 @@ QPixmap PixmapFromImage(QImage &&image) {
|
|||
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() {
|
||||
qunsetenv("QT_DEVICE_PIXEL_RATIO");
|
||||
qunsetenv("QT_SCALE_FACTOR");
|
||||
|
|
|
|||
|
|
@ -187,6 +187,10 @@ QPointer<const Widget> MakeWeak(not_null<const Widget*> object) {
|
|||
|
||||
[[nodiscard]] QPixmap PixmapFromImage(QImage &&image);
|
||||
|
||||
[[nodiscard]] bool IsContentVisible(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &rect = QRect());
|
||||
|
||||
void DisableCustomScaling();
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue