Qt 5.0以上mac版本一致存在这个bug,网上也有解决方案,一个是修改Qt源码重新编译,这个比较费事,另一个是将文件改为.mm后缀,使用oc代码:
NSView* view = (NSView*)this->winId();
NSWindow* wnd = [view window];
[wnd miniaturize:nil];
这句代码好像在qt5.2中可以解决,然后我测试了之后的所有版本(qt5.1-qt5.9)均失败。
所以这个方案不是合适方案。我给出一个通用的方案,也是使用oc:
不要使用
this->setWindowFlags(Qt::FramelessWindowHint);
这样的代码去修改Qt未无边框,改用oc:
//去掉系统标题栏(qt 在mac使用Qt::frameless标志有bug)
NSView* view = (NSView*)this->winId();
NSWindow* wndd = [view window];
wndd.titlebarAppearsTransparent = YES;
wndd.titleVisibility = NSWindowTitleHidden;
wndd.styleMask |= NSFullSizeContentViewWindowMask;
[[wndd standardWindowButton:NSWindowZoomButton] setHidden:YES];
[[wndd standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[wndd standardWindowButton:NSWindowCloseButton] setHidden:YES];
这样就实现了无边框,然后可以自己定义标题栏:
this->showMinimized();
this->showMaximized();
这样的函数功能也正常。
这里还有一个问题如果窗体开始是最大化显示时,调用
this->showMinimized(); 会调用一次showNomal函数,导致在恢复时与开始状态不一致 所以这里最小化最好也使用oc
NSView* view = (NSView*)this->winId();
NSWindow* wnd = [view window];
[wnd miniaturize:nil];
到这里问题就解决了么?
答案是否定的,上面的方法表面上却是解决了, 但是有个一个问题,会导致qt的内存异常,我测试大概要增长1.5倍,如果你的程序只有100M左右,那么着不算什么,上面的解决方案可用,如果你的程序本身就有500M+。这个方案就悲剧了。
QMacNativeWidget
这个类就是用来将qt的窗体潜入oc的。 大家应该知道我的思路了
上代码:
//以下oc代码为了解决qt设置无边框后 不能最小化的问题
/*******************oc start*************************/
@interface SGRoundView : NSView
@end
@implementation SGRoundView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
[[NSColor colorWithDeviceCyan:0.23 magenta:0.17 yellow:0.17 black:0 alpha:1] set];
NSRectFill(dirtyRect);
}
@end
@interface SGRoundWindow : NSWindow
@end
@implementation SGRoundWindow
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
{
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask backing:bufferingType defer:flag];
if ( self )
{
//设置窗口为无边界
[self setStyleMask:NSBorderlessWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask];
//设置窗口为透明
[self setOpaque:NO];
//设置背景无色
[self setBackgroundColor:[NSColor clearColor]];
//设置为点击背景可以移动窗口
[self setMovableByWindowBackground:YES];
[self setHasShadow:YES];
}
return self;
}
//实现圆角,直播晕是直角故这里不需要
//- (void) setContentView:(NSView *)aView
//{
// aView.wantsLayer = YES;
// aView.layer.frame = aView.frame;
// aView.layer.cornerRadius = 0;
// aView.layer.masksToBounds = YES;
// [super setContentView:aView];
//}
//如果不写此方法,生产的窗口上,添加的一些控件或文本处于editable
//加上此方法是使window变为keywindow
//一般是在无TitleBar时,也就是BorderlessWindow,才覆写此方法
//有TitleBar的Window默认是KeyWindow
- (BOOL)canBecomeKeyWindow
{
return YES;
}
-(BOOL)canBecomeMainWindow {
return YES;
}
@end
/*******************oc end*************************/QMacNativeWidget* changeWidgetToMacNativeWidge( QWidget * widget, int x = 0, int y = 0)
QMacNativeWidget* WindowMgr::changeWidgetToMacNativeWidget(QWidget *widget, int x, int y)
{
if(Q_NULLPTR == widget)
return Q_NULLPTR;
SGRoundWindow*nsWindow = [[SGRoundWindow alloc] initWithContentRect:NSMakeRect(x, y, widget->width(), widget->height())
styleMask:NSTitledWindowMask | NSClosableWindowMask
| NSMiniaturizableWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered defer:NO];
nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenPrimary;
QMacNativeWidget *nativeWidget = new QMacNativeWidget();
nativeWidget->setWindowFlags(Qt::FramelessWindowHint);
nativeWidget->move(0,0);
QVBoxLayout *layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(widget);
nativeWidget->setLayout(layout);
// Adjust Cocoa layouts
NSView *nativeWidgetView = reinterpret_cast<NSView *>(nativeWidget->winId());
NSView *contentView = [nsWindow contentView];
[contentView setAutoresizesSubviews:YES];
[nativeWidgetView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[nativeWidgetView setAutoresizesSubviews:YES];
[nativeWidgetView setAutoresizingMask:NSViewWidthSizable];
// Add the nativeWidget to the window.
[contentView addSubview:nativeWidgetView positioned:NSWindowAbove relativeTo:nil];
return nativeWidget;
}
{
if(Q_NULLPTR == widget)
return Q_NULLPTR;
NSWindow*nsWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(x, y, widget->width(), widget->height())
styleMask:NSTitledWindowMask | NSClosableWindowMask
| NSMiniaturizableWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered defer:NO];
//去掉标题栏
nsWindow.titlebarAppearsTransparent = YES;
nsWindow.titleVisibility = NSWindowTitleHidden;
nsWindow.styleMask |= NSFullSizeContentViewWindowMask;
[[nsWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
[[nsWindow standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[nsWindow standardWindowButton:NSWindowCloseButton] setHidden:YES];
QMacNativeWidget *nativeWidget = new QMacNativeWidget();
nativeWidget->setWindowFlags(Qt::FramelessWindowHint);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(widget);
nativeWidget->setLayout(layout);
// Adjust Cocoa layouts
NSView *nativeWidgetView = reinterpret_cast<NSView *>(nativeWidget->winId());
NSView *contentView = [nsWindow contentView];
[contentView setAutoresizesSubviews:YES];
[nativeWidgetView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[nativeWidgetView setAutoresizesSubviews:YES];
[nativeWidgetView setAutoresizingMask:NSViewWidthSizable];
// Add the nativeWidget to the window.
[contentView addSubview:nativeWidgetView positioned:NSWindowAbove relativeTo:nil];
// Show the window.
[nsWindow makeKeyAndOrderFront:nsWindow];
return nativeWidget;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow *mainwindow = new MainWindow();
mainwindow->setFixedSize(app.desktop()->width(), app.desktop()->height());
QWidget *widget = changeWidgetToMacNativeWidget(mainwindow, 0, 40);
widget->show();
return app.exec();
}
然后你就可以对MainWindow实现最小化,如放一个按钮,链接一下槽:
void MainWindow::minize()
{
[windo1 miniaturize:nil];
}
跑一下,点一下最小化,效果还可以跟系统的一样。
但还有一个问题,这里应该是
QMacNativeWidget
的一个bug,最小化有点级dock栏的按钮无法显示窗体,但是不要怕,我既然写了,就有解决方案:
connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(slot_restoreWindow(Qt::ApplicationState)));
应用程序激活的时候会信号,链接一下
void MainWindow:slot_restoreWindow (Qt::ApplicationState state)
{
if(state == Qt::ApplicationActive)
{
[window makeKeyAndOrderFront:window];
}
}
这里的window是
changeWidgetToMacNativeWidge返回的QMacNativeWidget所以要保存一下,QMacNativeWidget可以获取NSVIew,NSview可以获取到window
到这里这个问题算是解决了,期待各位更好的解决方案
不过还是期望Qt新版本能解决这个问题。毕竟这个bug已经出现好久了