Hello!
I’m trying to make a frameless GTK window with a Qt widget embedded inside it, but the widget ends up not matching the outline of the main GTK window. Is there any way to fix this?
Here’s a complete example of the code I’m using:
GtkMainWindow.pro
QT += core gui widgets
CONFIG += c++11
CONFIG += app_bundle
TEMPLATE = app
TARGET = gtkmainwindow
CONFIG += link_pkgconfig
PKGCONFIG += gtk+-3.0
SOURCES += main.cpp gtkmainwindow.cpp
HEADERS += gtkmainwindow.h
gtkmainwindow.h
#ifndef GTKMAINWINDOW_H
#define GTKMAINWINDOW_H
#include <QWidget>
class GtkMainWindow
{
public:
GtkMainWindow();
~GtkMainWindow();
void setCentralWidget(QWidget *w);
void show();
private:
class GtkMainWindowPrivate;
GtkMainWindowPrivate *pimpl;
};
#endif // GTKMAINWINDOW_H
gtkmainwindow.cpp
#include <gtk/gtk.h>
#include "gtkmainwindow.h"
#include <QVBoxLayout>
#include <gtk/gtkx.h>
gboolean on_configure_event(GtkWidget *wgt, GdkEvent *ev, gpointer data)
{
if (QWidget *w = (QWidget*)data) {
gint x = 0, y = 0;
gtk_window_get_size(GTK_WINDOW(wgt), &x, &y);
w->resize(x, y);
}
return FALSE;
}
class GtkMainWindow::GtkMainWindowPrivate
{
public:
GtkMainWindowPrivate() {}
~GtkMainWindowPrivate() {}
GtkWidget *window = nullptr;
QWidget *w = nullptr;
};
GtkMainWindow::GtkMainWindow() :
pimpl(new GtkMainWindowPrivate)
{
gtk_init(NULL, NULL);
pimpl->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pimpl->window), "GtkMainWindow");
gtk_window_set_default_size(GTK_WINDOW(pimpl->window), 600, 400);
gtk_window_set_position(GTK_WINDOW(pimpl->window), GtkWindowPosition::GTK_WIN_POS_CENTER);
g_signal_connect(G_OBJECT(pimpl->window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider, "decoration {border: 0px solid #ff0000; border-radius: 8px 8px 0px 0px;}", -1, NULL);
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
GtkWidget *header = gtk_header_bar_new();
gtk_window_set_titlebar(GTK_WINDOW(pimpl->window), header);
gtk_widget_destroy(header); // remove header
GtkWidget *socket = gtk_socket_new();
gtk_widget_show(socket);
gtk_container_add(GTK_CONTAINER(pimpl->window), socket);
pimpl->w = new QWidget(nullptr, Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
g_signal_connect(G_OBJECT(pimpl->window), "configure-event", G_CALLBACK(on_configure_event), pimpl->w);
QVBoxLayout *lut = new QVBoxLayout;
lut->setContentsMargins(0,0,0,0);
pimpl->w->setLayout(lut);
pimpl->w->createWinId();
gtk_socket_add_id(GTK_SOCKET(socket), (Window)pimpl->w->winId());
}
GtkMainWindow::~GtkMainWindow()
{
delete pimpl;
}
void GtkMainWindow::setCentralWidget(QWidget *w)
{
pimpl->w->layout()->addWidget(w);
}
void GtkMainWindow::show()
{
gtk_widget_show_all(pimpl->window);
pimpl->w->show();
}
main.cpp
#include <QApplication>
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QSpacerItem>
#include "gtkmainwindow.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
GtkMainWindow wnd;
QWidget *w = new QWidget;
w->setStyleSheet("QWidget{background: #555555;} QPushButton{min-height: 24px; background: #888888; border: 0px solid #00ffff;}");
QGridLayout *gl = new QGridLayout;
w->setLayout(gl);
gl->setContentsMargins(0,0,0,0);
QPushButton *btn = new QPushButton("Button");
gl->addWidget(btn, 0, 0, 1, 1);
gl->addItem(new QSpacerItem(5, 5, QSizePolicy::Expanding, QSizePolicy::Expanding), 1, 0, 1, 1);
gl->addWidget(new QPushButton("Button"), 2, 0, 1, 1);
wnd.setCentralWidget(w);
wnd.show();
return app.exec();
}
There was an idea to use gdk_window_shape_combine_region() but it is not clear how exactly to do this and whether it would help.