很多客户端为了追求美观,会自定义标题栏。
但是,Qt 5.4.0以及之前(不知道什么时候能修复)有这样一个Bug,Mac OS X系统下,窗口设置了FrameHintLess属性后,不能最小化。
这让人很生气。。。
结果,网上在4.7版本中已经提交了修复代码,不知道现在为毛还没有应用到发布版本中,这效率不说了。
我现在将修复代码搬出来,供大家参考。
版本:Qt5.3.2
修改的文件:qcocoawindow.mm
影响的工程:cocoa
修复步骤:
1. 替换文件qcocoawindow.mm;
2. 打开qt.pro
3. 找到qt/qtbase/src/plugins/platforms/cocoa.pro, rebuild;
4. 将编好的库替换到clang_64即可。
qcocoawindow.mm文件内容:
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qcocoawindow.h"
#include "qcocoaintegration.h"
#include "qnswindowdelegate.h"
#include "qcocoaautoreleasepool.h"
#include "qcocoaeventdispatcher.h"
#include "qcocoaglcontext.h"
#include "qcocoahelpers.h"
#include "qcocoanativeinterface.h"
#include "qnsview.h"
#include <QtCore/qfileinfo.h>
#include <QtCore/private/qcore_mac_p.h>
#include <qwindow.h>
#include <private/qwindow_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformscreen.h>
#include <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
#include <QDebug>
enum {
defaultWindowWidth = 160,
defaultWindowHeight = 160
};
static bool isMouseEvent(NSEvent *ev)
{
switch ([ev type]) {
case NSLeftMouseDown:
case NSLeftMouseUp:
case NSRightMouseDown:
case NSRightMouseUp:
case NSMouseMoved:
case NSLeftMouseDragged:
case NSRightMouseDragged:
return true;
default:
return false;
}
}
@interface NSWindow (CocoaWindowCategory)
- (NSRect) legacyConvertRectFromScreen:(NSRect) rect;
@end
@implementation NSWindow (CocoaWindowCategory)
- (NSRect) legacyConvertRectFromScreen:(NSRect) rect
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) {
return [self convertRectFromScreen: rect];
}
#endif
NSRect r = rect;
r.origin = [self convertScreenToBase:rect.origin];
return r;
}
@end
@implementation QNSWindowHelper
@synthesize window = _window;
@synthesize platformWindow = _platformWindow;
@synthesize grabbingMouse = _grabbingMouse;
@synthesize releaseOnMouseUp = _releaseOnMouseUp;
- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow
{
self = [super init];
if (self) {
_window = window;
_platformWindow = platformWindow;
_window.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:_platformWindow];
// Prevent Cocoa from releasing the window on close. Qt
// handles the close event asynchronously and we want to
// make sure that m_nsWindow stays valid until the
// QCocoaWindow is deleted by Qt.
[_window setReleasedWhenClosed:NO];
}
return self;
}
- (void)handleWindowEvent:(NSEvent *)theEvent
{
QCocoaWindow *pw = self.platformWindow;
if (pw && pw->m_forwardWindow) {
if (theEvent.type == NSLeftMouseUp || theEvent.type == NSLeftMouseDragged) {
QNSView *forwardView = pw->m_qtView;
if (theEvent.type == NSLeftMouseUp) {
[forwardView mouseUp:theEvent];
pw->m_forwardWindow = 0;
} else {
[forwardView mouseDragged:theEvent];
}
}
if (!pw->m_isNSWindowChild && theEvent.type == NSLeftMouseDown) {
pw->m_forwardWindow = 0;
}
}
if (theEvent.type == NSLeftMouseDown) {
self.grabbingMouse = YES;
} else if (theEvent.type == NSLeftMouseUp) {
self.grabbingMouse = NO;
if (self.releaseOnMouseUp) {
[self detachFromPlatformWindow];
[self.window release];
return;
}
}
// The call to -[NSWindow sendEvent] may result in the window being deleted
// (e.g., when closing the window by pressing the title bar close button).
[self retain];
[self.window superSendEvent:theEvent];
bool windowStillAlive = self.window != nil; // We need to read before releasing
[self release];
if (!windowStillAlive)
return;
if (!self.window.delegate)
return; // Already detached, pending NSAppKitDefined event
if (pw && pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
NSPoint loc = [theEvent locationInWindow];
NSRect windowFrame = [self.window legacyConvertRectFromScreen:[self.window frame]];
NSRect contentFrame = [[self.window contentView] frame];
if (NSMouseInRect(loc, windowFrame, NO) &&
!NSMouseInRect(loc, contentFrame, NO))
{
QNSView *contentView = (QNSView *)pw->contentView();
[contentView handleFrameStrutMouseEvent: theEvent];
}
}
}
- (void)detachFromPlatformWindow
{
_platformWindow = 0;
[self.window.delegate release];
self.window.delegate = nil;
}
- (void)clearWindow
{
if (_window) {
QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher());
if (cocoaEventDispatcher) {
QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
cocoaEventDispatcherPrivate->removeQueuedUserInputEvents([_window windowNumber]);
}
_window = nil;
}
}
- (void)dealloc
{
_window = nil;
_platformWindow = 0;
[super dealloc];
}
@end
@implementation QNSWindow
@synthesize helper = _helper;
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw
{
self = [super initWithContentRect:contentRect
styleMask:windowStyle
backing:NSBackingStoreBuffered
defer:NO]; // Deferring window creation breaks OpenGL (the GL context is
// set up before the window is shown and needs a proper window)
if (self) {
_helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
}
return self;
}
- (BOOL)canBecomeKeyWindow
{
// Prevent child NSWindows from becoming the key window in
// order keep the active apperance of the top-level window.
QCocoaWindow *pw = self.helper.platformWindow;
if (!pw || pw->m_isNSWindowChild)
return NO;
if (pw->shouldRefuseKeyWindowAndFirstResponder())
return NO;
// The default implementation returns NO for title-bar less windows,
// override and return yes here to make sure popup windows such as
// the combobox popup can become the key window.
return YES;
}
- (BOOL)canBecomeMainWindow
{
BOOL canBecomeMain = YES; // By default, windows can become the main window
// Windows with a transient parent (such as combobox popup windows)
// cannot become the main window:
QCocoaWindow *pw = self.helper.platformWindow;
if (!pw || pw->m_isNSWindowChild || pw->window()->transientParent())
canBecomeMain = NO;
return canBecomeMain;
}
- (void) sendEvent: (NSEvent*) theEvent
{
[self.helper handleWindowEvent:theEvent];
}
- (void)superSendEvent:(NSEvent *)theEvent
{
[super sendEvent:theEvent];
}
- (void)closeAndRelease
{
[self close];
if (self.helper.grabbingMouse) {
self.helper.releaseOnMouseUp = YES;
} else {
[self.helper detachFromPlatformWindow];
[self release];
}
}
- (void)dealloc
{
[_helper clearWindow];
[_helper release];
_helper = nil;
[super dealloc];
}
@end
@implementation QNSPanel
@synthesize helper = _helper;
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw
{
self = [super initWithContentRect:contentRect
styleMask:windowStyle
backing:NSBackingStoreBuffered
defer:NO]; // Deferring window creation breaks OpenGL (the GL context is
// set up before the window is shown and needs a proper window)
if (self) {
_helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
}
return self;
}
- (BOOL)canBecomeKeyWindow
{
QCocoaWindow *pw = self.helper.platformWindow;
if (!pw)
return NO;
if (pw->shouldRefuseKeyWindowAndFirstResponder())
return NO;
// Only tool or dialog windows should become key:
Qt::WindowType type = pw->window()->type();
if (type == Qt::Tool || type == Qt::Dialog)
return YES;
return NO;
}
- (void) sendEvent: (NSEvent*) theEvent
{
[self.helper handleWindowEvent:theEvent];
}
- (void)superSendEvent:(NSEvent *)theEvent
{
[super sendEvent:theEvent];
}
- (void)closeAndRelease
{
[self.helper detachFromPlatformWindow];
[self close];
[self release];
}
- (void)dealloc
{
[_helper clearWindow];
[_helper release];
_helper = nil;
[super dealloc];
}
@end
const int QCocoaWindow::NoAlertRequest = -1;
QCocoaWindow::QCocoaWindow(QWindow *tlw)
: QPlatformWindow(tlw)
, m_contentView(nil)
, m_qtView(nil)
, m_nsWindow(0)
, m_forwardWindow(0)
, m_contentViewIsEmbedded(false)
, m_contentViewIsToBeEmbedded(false)
, m_parentCocoaWindow(0)
, m_isNSWindowChild(false)
, m_effectivelyMaximized(false)
, m_synchedWindowState(Qt::WindowActive)
, m_windowModality(Qt::NonModal)
, m_windowUnderMouse(false)
, m_inConstructor(true)
, m_inSetVisible(false)
, m_glContext(0)
, m_menubar(0)
, m_windowCursor(0)
, m_hasModalSession(false)
, m_frameStrutEventsEnabled(false)
, m_geometryUpdateExposeAllowed(false)
, m_isExposed(false)
, m_registerTouchCount(0)
, m_resizableTransientParent(false)
, m_hiddenByClipping(false)
, m_hiddenByAncestor(false)
, m_alertRequest(NoAlertRequest)
, monitor(nil)
, m_drawContentBorderGradient(false)
, m_topContentBorderThickness(0)
, m_bottomContentBorderThickness(0)
, m_normalGeometry(QRect(0,0,-1,-1))
{
#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
qDebug() << "QCocoaWindow::QCocoaWindow" << this;
#endif
QCocoaAutoReleasePool pool;
if (tlw->type() == Qt::ForeignWindow) {
NSView *foreignView = (NSView *)WId(tlw->property("_q_foreignWinId").value<WId>());
setContentView(foreignView);
} else {
m_qtView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this];
m_contentView = m_qtView;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
// Enable high-dpi OpenGL for retina displays. Enabling has the side
// effect that Cocoa will start calling glViewport(0, 0, width, height),
// overriding any glViewport calls in application code. This is usually not a
// problem, except if the appilcation wants to have a "custom" viewport.
// (like the hellogl example)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7
&& tlw->supportsOpenGL()) {
BOOL enable = qt_mac_resolveOption(YES, tlw, "_q_mac_wantsBestResolutionOpenGLSurface",
"QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
[m_contentView setWantsBestResolutionOpenGLSurface:enable];
}
#endif
BOOL enable = qt_mac_resolveOption(NO, tlw, "_q_mac_wan