❝本文翻译自: https://www.qt.io/blog/custom-window-decorations
❞
原作者: Johan Helsing
这只是Qt 5.15中一个新功能的快速更新,我对此感到非常兴奋。传统上,窗口装饰一直是一件很无聊的事情。标题栏,边框,最小化,最大化,调整大小和退出,差不多就是这样了。但是,近来,应用程序越来越倾向于在其装饰中包括特定应用程序的UI和主题。比如:
MacOS已经这样做了一段时间。
Chrome以及几乎所有其他网络浏览器也是如此。
将菜单嵌入装饰中可以节省大量屏幕空间。
或者对于品牌或设计目的而言可能很重要。
不幸的是,Qt以前是不可能实现这些事情的。但是,可以去除窗口上的装饰物,即:
Window {
flags: Qt.FramelessWindowHint
}
但这给您留下了没有装饰的窗口。因此无法移动或调整大小。如果您随后尝试执行窗口移动或通过抓住鼠标并手动设置窗口大小和位置来调整自己的大小,您会很快发现它确实感觉不太好。窗口管理器通常对窗口的移动或调整大小具有非常特定的行为。常见的约定是拖动到顶部以最大化,向左/向右拖动以平铺,捕捉到其他窗口或任务栏,如果两个窗口彼此并排平铺,则同时调整两个窗口的大小,依此类推。
讲道理,我们之前确实为此提供了一个帮助:QSizeGrip。它使您可以调整窗口的任何给定角的大小,但它仅适用于角,而不适用于窗口边缘,并且仅适用于窗口小部件应用程序。
在Qt 5.15中,我们向QWindow添加了两个新方法:startSystemMove和startSystemResize。这些方法要求窗口管理器接管并启动本机调整大小或移动操作。这意味着捕捉,平铺等功能可以正常使用,并且在QML中实现标题栏几乎成了一种形式:
DragHandler {
onActiveChanged: if (active) window.startSystemMove();
target: null
}
将这段代码放在QtQuick中,将使任何拖动操作都触发本机窗口移动操作。
startSystemResize的工作原理类似,不同之处在于它需要一个Qt::Edges参数,该参数是您抓取的窗口边缘的位字段。例如:
startSystemResize(Qt.RightEdge | Qt.BottomEdge)
这也非常方便,因为您可以轻松地为所有四个窗口边缘都拥有一个处理程序,并像这样建立edges参数:
DragHandler {
id: resizeHandler
grabPermissions: TapHandler.TakeOverForbidden
target: null
onActiveChanged: if (active) {
const p = resizeHandler.centroid.position;
let e = 0;
if (p.x < border) e |= Qt.LeftEdge;
if (p.x >= width - border) e |= Qt.RightEdge;
if (p.y < border) e |= Qt.TopEdge;
if (p.y >= height - border) e |= Qt.BottomEdge;
window.startSystemResize(e);
}
}
如果您想了解如何使用它的完整示例(https://github.com/johanhelsing/qt-csd-demo),我使用新的API制作了一个网络浏览器的模型。
请注意,尽管这是一个跨平台的API,但并非所有平台都支持它。当前,Wayland,X11,macOS和Windows均支持startSystemMove,而Wayland,X11和Windows则支持startSystemResize,但macOS不支持。
为了解决这个问题,两个方法都返回一个布尔值,该布尔值指示是否支持该操作。这意味着,如果您也想在macOS上实现调整大小,则必须检查startSystemResize的返回值。
if (!window.startSystemResize(edges)) {
// your fallback code for setting window.width/height manually
}
Qt的进一步工作将是尽量兼容上述问题。另一个改进的领域是与窗口管理器就应使用客户端还是服务器端窗口装饰进行协商。某些应用程序可能希望同时支持这两种模式,并让窗口管理器决定,但目前尚无法实现。一旦设置了FramelessWindowHint,就不会有服务器端装饰。
第三个区域是窗口阴影。至少在Wayland上,应将阴影绘制为窗口装饰的一部分。虽然我们可以使用QtQuick绝对可以绘制阴影,但是目前尚无办法告诉QPA插件表面的哪一部分是阴影,哪一部分是窗框,这意味着如果您尝试绘制阴影,则窗口管理器将当前考虑窗口的阴影部分,这将使其他窗口的平铺和对齐变得混乱。在其他平台上,阴影通常是由窗口管理器绘制的,即使对于客户端装饰的窗口也是如此,因此这是一个棘手的问题。
最后,非常感谢帮助在各种平台上测试API的每个人!我真的希望人们能用它构建一些有趣的东西。
更多精彩内容请关注公众号「Qt君」。