QtCanpool 3.3.0
载入中...
搜索中...
未找到
dockutils.cpp
浏览该文件的文档.
1
6#include "dockutils.h"
7#include "dockmanager.h"
8#include "docksplitter.h"
9
10#include <QPainter>
11#include <QSplitter>
12#include <QDebug>
13
14#ifdef QX_DOCK_DEBUG
15#include <QBoxLayout>
16#include "dockcontainer.h"
17#include "dockpanel.h"
18#include "dockwidget.h"
20#endif
21
22#if QX_DOCK_X11
23#include <QSettings>
24#include <QFile>
25#include <QApplication>
26#include <qpa/qplatformnativeinterface.h>
27#endif
28
30
31namespace internal
32{
33const int FloatingWidgetDragStartEvent = QEvent::registerEventType();
34const int DockedWidgetDragStartEvent = QEvent::registerEventType();
35
41QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
42{
43 QPixmap transparentPixmap(source.size());
44 transparentPixmap.fill(Qt::transparent);
45 QPainter p(&transparentPixmap);
46 p.setOpacity(opacity);
47 p.drawPixmap(0, 0, source);
48 return transparentPixmap;
49}
50
64void setButtonIcon(QAbstractButton *b, QStyle::StandardPixmap pixmap, Qx::DockIcon iconId)
65{
66 // First we try to use custom icons if available
67 QIcon Icon = DockManager::iconProvider().customIcon(iconId);
68 if (!Icon.isNull()) {
69 b->setIcon(Icon);
70 return;
71 }
72
73#if QX_DOCK_X11
74 b->setIcon(b->style()->standardIcon(pixmap));
75#else
76 // The standard icons does not look good on high DPI screens so we create
77 // our own "standard" icon here.
78 QPixmap normalPixmap = b->style()->standardPixmap(pixmap, 0, b);
79 Icon.addPixmap(createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
80 Icon.addPixmap(normalPixmap, QIcon::Normal);
81 b->setIcon(Icon);
82#endif
83}
84
86{
87 switch (area) {
89 return DockInsertParam(Qt::Vertical, false);
91 return DockInsertParam(Qt::Horizontal, true);
94 return DockInsertParam(Qt::Vertical, true);
96 return DockInsertParam(Qt::Horizontal, false);
97 default:
98 DockInsertParam(Qt::Vertical, false);
99 } // switch (area)
100
101 return DockInsertParam(Qt::Vertical, false);
102}
103
109{
110 if (!w) {
111 return;
112 }
113 w->style()->unpolish(w);
114 w->style()->polish(w);
115
117 return;
118 }
119
120 QList<QWidget *> childrens = w->findChildren<QWidget *>(
121 QString(), (RepolishDirectChildren == options) ? Qt::FindDirectChildrenOnly : Qt::FindChildrenRecursively);
122
123 for (auto c : childrens) {
124 c->style()->unpolish(c);
125 c->style()->polish(c);
126 }
127}
128
134{
135 while (splitter && splitter->isVisible()) {
136 if (!splitter->hasVisibleContent()) {
137 splitter->hide();
138 }
139 splitter = internal::findParent<DockSplitter *>(splitter);
140 }
141}
142
146void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
147{
148 int index = splitter->indexOf(from);
149 from->setParent(nullptr);
150 splitter->insertWidget(index, to);
151}
152
154{
155 switch (area) {
158 return true;
161 return false;
162 default:
163 return false;
164 }
165
166 return false;
167}
168
170{
171 return toSideBarArea(area) != Qx::DockSideBarNone;
172}
173
175{
176 switch (area) {
178 return Qx::DockSideBarLeft;
182 return Qx::DockSideBarTop;
185 default:
186 return Qx::DockSideBarNone;
187 }
188
189 return Qx::DockSideBarNone;
190}
191
192#ifdef QX_DOCK_DEBUG
193void setBackgroudColor(QWidget *w)
194{
195 QPalette pe = w->palette();
196 if (qobject_cast<DockContainer *>(w)) {
197 pe.setColor(QPalette::Window, Qt::red);
198 } else if (qobject_cast<DockPanel *>(w)) {
199 pe.setColor(QPalette::Window, Qt::green);
200 } else if (qobject_cast<DockWidget *>(w)) {
201 pe.setColor(QPalette::Window, Qt::blue);
202 } else {
203 pe.setColor(QPalette::Window, Qt::yellow);
204 }
205 w->setPalette(pe);
206 w->setAutoFillBackground(true);
207 w->layout()->setContentsMargins(2, 2, 2, 2);
208}
209#endif
210
211#if QX_DOCK_X11
212static QString _window_manager;
213static QHash<QString, xcb_atom_t> _xcb_atom_cache;
214
215bool is_platform_x11()
216{
217 return QGuiApplication::platformName() == QLatin1String("xcb");
218}
219
220xcb_connection_t *x11_connection()
221{
222 if (!qApp)
223 return nullptr;
224 QPlatformNativeInterface *native = qApp->platformNativeInterface();
225 if (!native)
226 return nullptr;
227
228 void *connection = native->nativeResourceForIntegration(QByteArray("connection"));
229 return reinterpret_cast<xcb_connection_t *>(connection);
230}
231
232xcb_atom_t xcb_get_atom(const char *name)
233{
234 if (!is_platform_x11()) {
235 return XCB_ATOM_NONE;
236 }
237 auto key = QString(name);
238 if (_xcb_atom_cache.contains(key)) {
239 return _xcb_atom_cache[key];
240 }
241 xcb_connection_t *connection = x11_connection();
242 xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
243 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, nullptr);
244 if (!reply) {
245 return XCB_ATOM_NONE;
246 }
247 xcb_atom_t atom = reply->atom;
248 if (atom == XCB_ATOM_NONE) {
249 QX_DOCK_PRINT("Unknown Atom response from XServer: " << name);
250 } else {
251 _xcb_atom_cache.insert(key, atom);
252 }
253 free(reply);
254 return atom;
255}
256
257void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
258{
259 auto connection = x11_connection();
260 xcb_atom_t type_atom = xcb_get_atom(type);
261 xcb_atom_t prop_atom = xcb_get_atom(prop);
262 xcb_client_message_event_t event;
263 event.response_type = XCB_CLIENT_MESSAGE;
264 event.format = 32;
265 event.sequence = 0;
266 event.window = window;
267 event.type = type_atom;
268 event.data.data32[0] = set ? 1 : 0;
269 event.data.data32[1] = prop_atom;
270 event.data.data32[2] = prop2 ? xcb_get_atom(prop2) : 0;
271 event.data.data32[3] = 0;
272 event.data.data32[4] = 0;
273
274 xcb_send_event(connection, 0, window,
275 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
276 XCB_EVENT_MASK_PROPERTY_CHANGE,
277 (const char *)&event);
278 xcb_flush(connection);
279}
280
281xcb_get_property_reply_t *_xcb_get_props(WId window, const char *type, unsigned int atom_type)
282{
283 if (!is_platform_x11()) {
284 return nullptr;
285 }
286 xcb_connection_t *connection = x11_connection();
287 xcb_atom_t type_atom = xcb_get_atom(type);
288 if (type_atom == XCB_ATOM_NONE) {
289 return nullptr;
290 }
291 xcb_get_property_cookie_t request =
292 xcb_get_property_unchecked(connection, 0, window, type_atom, atom_type, 0, 1024);
293 xcb_get_property_reply_t *reply = xcb_get_property_reply(connection, request, nullptr);
294 if (reply && reply->type != atom_type) {
295 QX_DOCK_PRINT("ATOM TYPE MISMATCH (" << type << "). Expected: " << atom_type << " but got " << reply->type);
296 free(reply);
297 return nullptr;
298 }
299 return reply;
300}
301
302template <typename T> void xcb_get_prop_list(WId window, const char *type, QVector<T> &ret, unsigned int atom_type)
303{
304 xcb_get_property_reply_t *reply = _xcb_get_props(window, type, atom_type);
305 if (reply && reply->format == 32 && reply->type == atom_type && reply->value_len > 0) {
306 const xcb_atom_t *data = static_cast<const T *>(xcb_get_property_value(reply));
307 ret.resize(reply->value_len);
308 memcpy((void *)&ret.first(), (void *)data, reply->value_len * sizeof(T));
309 }
310 free(reply);
311}
312
313QString xcb_get_prop_string(WId window, const char *type)
314{
315 QString ret;
316 // try utf8 first
317 xcb_atom_t utf_atom = xcb_get_atom("UTF8_STRING");
318 if (utf_atom != XCB_ATOM_NONE) {
319 xcb_get_property_reply_t *reply = _xcb_get_props(window, type, utf_atom);
320 if (reply && reply->format == 8 && reply->type == utf_atom) {
321 const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
322 ret = QString::fromUtf8(value, xcb_get_property_value_length(reply));
323 free(reply);
324 return ret;
325 }
326 free(reply);
327 }
328 // Fall back to XCB_ATOM_STRING
329 xcb_get_property_reply_t *reply = _xcb_get_props(window, type, XCB_ATOM_STRING);
330 if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
331 const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
332 ret = QString::fromLatin1(value, xcb_get_property_value_length(reply));
333 }
334 free(reply);
335 return ret;
336}
337
338bool xcb_dump_props(WId window, const char *type)
339{
340 QVector<xcb_atom_t> atoms;
341 xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
342 qDebug() << "\n\n!!!" << type << " - " << atoms.length();
343 xcb_connection_t *connection = x11_connection();
344 for (auto atom : atoms) {
345 auto foo = xcb_get_atom_name(connection, atom);
346 auto bar = xcb_get_atom_name_reply(connection, foo, nullptr);
347 qDebug() << "\t" << xcb_get_atom_name_name(bar);
348 free(bar);
349 }
350 return true;
351}
352
353void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
354{
355 if (!is_platform_x11()) {
356 return;
357 }
358 xcb_atom_t prop_atom = xcb_get_atom(prop);
359 xcb_atom_t type_atom = xcb_get_atom(type);
360 if (prop_atom == XCB_ATOM_NONE || type_atom == XCB_ATOM_NONE) {
361 return;
362 }
363 QVector<xcb_atom_t> atoms;
364 xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
365 int index = atoms.indexOf(prop_atom);
366 if (state && index == -1) {
367 atoms.push_back(prop_atom);
368 } else if (!state && index >= 0) {
369 atoms.remove(index);
370 }
371 xcb_connection_t *connection = x11_connection();
372 xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(),
373 atoms.constData());
374 xcb_flush(connection);
375}
376
377QString detectWindowManagerX11()
378{
379 // Tries to detect the windowmanager via X11.
380 // See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000
381 if (!is_platform_x11()) {
382 return "UNKNOWN";
383 }
384 xcb_connection_t *connection = x11_connection();
385 xcb_screen_t *first_screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
386 if (!first_screen) {
387 QX_DOCK_PRINT("No screen found via XCB.");
388 return "UNKNOWN";
389 }
390 // Get supporting window ()
391 xcb_window_t root = first_screen->root;
392 xcb_window_t support_win = 0;
393 QVector<xcb_window_t> sup_windows;
394 xcb_get_prop_list(root, "_NET_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_WINDOW);
395 if (sup_windows.length() == 0) {
396 // This doesn't seem to be in use anymore, but wmctrl does the same so lets play safe.
397 // Both XCB_ATOM_CARDINAL and XCB_ATOM_WINDOW break down to a uint32_t, so reusing sup_windows should be fine.
398 xcb_get_prop_list(root, "_WIN_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_CARDINAL);
399 }
400 if (sup_windows.length() == 0) {
401 QX_DOCK_PRINT("Failed to get the supporting window on non EWMH comform WM.");
402 return "UNKNOWN";
403 }
404 support_win = sup_windows[0];
405 QString ret = xcb_get_prop_string(support_win, "_NET_WM_NAME");
406 if (ret.length() == 0) {
407 QX_DOCK_PRINT("Empty WM name occurred.");
408 return "UNKNOWN";
409 }
410 return ret;
411}
412
413QString windowManager()
414{
415 if (_window_manager.length() == 0) {
416 _window_manager = detectWindowManagerX11();
417 }
418 return _window_manager;
419}
420#endif
421
422} // internal
423
bool hasVisibleContent() const
#define QX_DOCK_PRINT(s)
Copyleft (C) 2024 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
Definition dockutils.h:24
void setButtonIcon(QAbstractButton *b, QStyle::StandardPixmap pixmap, Qx::DockIcon iconId)
Helper function to set the icon of a certain button.
Definition dockutils.cpp:64
const int DockedWidgetDragStartEvent
Definition dockutils.cpp:34
bool isHorizontalSideBarArea(Qx::DockSideBarArea area)
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
Replace the from widget in the given splitter with the To widget
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
Creates a semi transparent pixmap from the given pixmap Source.
Definition dockutils.cpp:41
Qx::DockSideBarArea toSideBarArea(Qx::DockWidgetArea area)
DockInsertParam dockAreaInsertParameters(Qx::DockWidgetArea area)
Definition dockutils.cpp:85
void repolishStyle(QWidget *w, RepolishChildOptions options)
Calls unpolish() / polish for the style of the given widget to update stylesheet if a property change...
void hideEmptyParentSplitters(DockSplitter *splitter)
This function walks the splitter tree upwards to hides all splitters that do not have visible content
bool isSideBarArea(Qx::DockWidgetArea area)
const int FloatingWidgetDragStartEvent
Definition dockutils.cpp:33
DockSideBarArea
@ DockSideBarTop
@ DockSideBarBottom
@ DockSideBarLeft
@ DockSideBarRight
@ DockSideBarNone
DockWidgetArea
@ LeftAutoHideArea
@ BottomDockWidgetArea
@ RightAutoHideArea
@ RightDockWidgetArea
@ LeftDockWidgetArea
@ TopDockWidgetArea
@ BottomAutoHideArea
@ CenterDockWidgetArea
@ TopAutoHideArea
#define QX_DOCK_END_NAMESPACE
#define QX_DOCK_BEGIN_NAMESPACE
QxLogger::Options options
Definition qxlogger.cpp:24
const char * name
Definition qxtheme.cpp:25
QColor window