谢... 忘记 哪个大佬一年前邀请我了,看了半天大家都实现的Windows上跟Mac上的无边框的解决方案,这里我就抛砖引玉,上个Linux的无边框的解决方案吧。

众所周知,Linux是基于X协议的,所以这里要写x函数,本次使用xlib来实现。你也可以用xcb的函数。

是的我又犯懒了,本来想自己写xlib的函数的,发现deepin的dktwidget里已经有对应的实现了,这里我们把他偷过来

https://github.com/linuxdeepin/dtkwidget/tree/master/src/platforms/x11

先上实现,这里上关键代码,重写这三个函数就可以达到效果

void FLWidget_Linux::mouseMoveEvent(QMouseEvent *event)
{
#ifdef Q_OS_LINUX
    const int x = event->x();
    const int y = event->y();

    if (resizingCornerEdge == XUtils::CornerEdge::kInvalid)
    {
        XUtils::UpdateCursorShape(this, x, y, this->layout()->contentsMargins(), ResizeHandleWidth);
    }
#endif

    return QWidget::mouseMoveEvent(event);
}

void FLWidget_Linux::mousePressEvent(QMouseEvent *event)
{
#ifdef Q_OS_LINUX
    const int x = event->x();
    const int y = event->y();
    if (event->button() == Qt::LeftButton)
    {
        const XUtils::CornerEdge ce = XUtils::GetCornerEdge(this, x, y, this->layout()->contentsMargins(), ResizeHandleWidth);
        if (ce != XUtils::CornerEdge::kInvalid)
        {
            resizingCornerEdge = ce;
            //send x11 move event dont send mouserrelease event
            XUtils::SendButtonRelease(this, event->pos(), event->globalPos());
            XUtils::StartResizing(this, QCursor::pos(), ce);
        }
    }
#endif
    return QWidget::mousePressEvent(event);
}

void FLWidget_Linux::resizeEvent(QResizeEvent *e)
{
#ifdef Q_OS_LINUX
    int resizeHandleWidth = ResizeHandleWidth;
    XUtils::SetWindowExtents(this, this->layout()->contentsMargins(), resizeHandleWidth);
#endif
    return QWidget::resizeEvent(e);
}

void FLWidget_Linux::mouseReleaseEvent(QMouseEvent *event)
{
#ifdef Q_OS_LINUX
    resizingCornerEdge = XUtils::CornerEdge::kInvalid;
#endif
    return QWidget::mouseReleaseEvent(event);
}

这里有个小坑,使用xlib的move之后,Qt会收不到鼠标抬起的事件,所以这里我又在deepin的代码里加了个新的函数

 //XUtils::SendButtonRelease(this, event->pos(), event->globalPos());

void SendButtonRelease(const QWidget *widget,
                                     const QPoint &pos, const QPoint &globalPos)
{
    const auto display = QX11Info::display();
    const auto screen = QX11Info::appScreen();

    XEvent xevent;
    memset(&xevent, 0, sizeof(XEvent));

    xevent.type = ButtonRelease;
    xevent.xbutton.button = Button1;
    xevent.xbutton.window = widget->effectiveWinId();
    xevent.xbutton.x = pos.x();
    xevent.xbutton.y = pos.y();
    xevent.xbutton.x_root = globalPos.x();
    xevent.xbutton.y_root = globalPos.y();
    xevent.xbutton.display = display;

    XSendEvent(display, widget->effectiveWinId(), False, ButtonReleaseMask, &xevent);
    XFlush(display);
}

来手动发送下这个事件

Ubuntu

ubuntu的无边框很有意思,你只需要把Qt的flag设置成 Qt::FramelessWindowHint ,不用重写x消息就能够自己无边框可移动。

Qt5.15

据说Qt5.15的QWindow类自带了无边框可移动的flag,可以等等这个feature。

未实现的

至于最大化,最小化,全屏之类的,deepin的代码里都封装好了对应的x函数。我又犯懒了,不想再加上自定义的标题栏,这个就算是抛砖引玉吧

上github代码链接
https://github.com/CryFeiFei/FLWidget

也顺便实现了下windows的无边框的实现,欢迎star。

欢迎关注我的小程序,小程序内容与网站自动保持同步

欢迎关注我的微信公众号,本网站所有的文章以及更新以后都会手动同步到微信公众号上。


公交车司机终于在众人的指责中将座位让给了老太太