macOS: Tighten up window icon and represented filename handling

Window icons on macOS are tied to document windows, and should not show
up unless a represented filename has also been set according to the HIG.

We follow this, and no longer create a document button based on the window
title if one did no exists. We also fall back to using the filename of
the file if a title has not been set, including being able to restore
this default title after setting a custom title.

The icon is no longer reset to nil after setting a QIcon(), which would
remove the icon completely, but instead we restore the default behavior
of showing the default filetype icon of the file.

Finally, the two callbacks in QNSWindowDelegate dealing with the document
icon/title popups and drags have been taught to look for spaces in the
represented filename. This allows clients who really want the old
behavior of setting an icon without caring about the filename to set
the filename to a single space character, which will prevent the
dropdowna and drag from occurring.

The reason for not tying this behavior to the existence of the file in
the filesystem is that being able to represent a file that is yet to
be saved is a valid usecase.

Task-number: QTBUG-63338
Change-Id: I7d4377c6358226fb6058f8c2b3c28588eec77ce5
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Tor Arne Vestbø 2017-09-28 15:46:45 +02:00
parent d892292409
commit bccbb52b7a
4 changed files with 38 additions and 20 deletions

View File

@ -971,6 +971,11 @@ QString QWindow::filePath() const
The window icon might be used by the windowing system for example to
decorate the window, and/or in the task switcher.
\note On \macos, the window title bar icon is meant for windows representing
documents, and will only show up if a file path is also set.
\sa setFilePath()
*/
void QWindow::setIcon(const QIcon &icon)
{
@ -983,7 +988,7 @@ void QWindow::setIcon(const QIcon &icon)
}
/*!
\brief Sets the window's icon in the windowing system
\brief Returns the window's icon in the windowing system
\sa setIcon()
*/

View File

@ -285,8 +285,6 @@ public: // for QNSView
};
QHash<quintptr, BorderRange> m_contentBorderAreas; // identifer -> uppper/lower
QHash<quintptr, bool> m_enabledContentBorderAreas; // identifer -> enabled state (true/false)
bool m_hasWindowFilePath;
};
#ifndef QT_NO_DEBUG_STREAM

View File

@ -157,7 +157,6 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
, m_drawContentBorderGradient(false)
, m_topContentBorderThickness(0)
, m_bottomContentBorderThickness(0)
, m_hasWindowFilePath(false)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window();
@ -594,6 +593,11 @@ void QCocoaWindow::setWindowTitle(const QString &title)
QMacAutoReleasePool pool;
m_view.window.title = title.toNSString();
if (title.isEmpty() && !window()->filePath().isEmpty()) {
// Clearing the title should restore the default filename
setWindowFilePath(window()->filePath());
}
}
void QCocoaWindow::setWindowFilePath(const QString &filePath)
@ -602,9 +606,14 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath)
return;
QMacAutoReleasePool pool;
QFileInfo fi(filePath);
[m_view.window setRepresentedFilename:fi.exists() ? filePath.toNSString() : @""];
m_hasWindowFilePath = fi.exists();
if (window()->title().isNull())
[m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
else
m_view.window.representedFilename = filePath.toNSString();
// Changing the file path may affect icon visibility
setWindowIcon(window()->icon());
}
void QCocoaWindow::setWindowIcon(const QIcon &icon)
@ -612,23 +621,21 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
if (!isContentView())
return;
NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
if (!iconButton) {
// Window icons are only supported on macOS in combination with a document filePath
return;
}
QMacAutoReleasePool pool;
NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
if (iconButton == nil) {
if (icon.isNull())
return;
NSString *title = window()->title().toNSString();
[m_view.window setRepresentedURL:[NSURL fileURLWithPath:title]];
iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
}
if (icon.isNull()) {
[iconButton setImage:nil];
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
[iconButton setImage:[workspace iconForFile:m_view.window.representedFilename]];
} else {
QPixmap pixmap = icon.pixmap(QSize(22, 22));
NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
[iconButton setImage:image];
[image release];
[iconButton setImage:[image autorelease]];
}
}

View File

@ -44,6 +44,8 @@
#include <qpa/qplatformscreen.h>
#include <qpa/qwindowsysteminterface.h>
static QRegExp whitespaceRegex = QRegExp(QStringLiteral("\\s*"));
@implementation QNSWindowDelegate
- (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow
@ -98,7 +100,10 @@
{
Q_UNUSED(window);
Q_UNUSED(menu);
return m_cocoaWindow && m_cocoaWindow->m_hasWindowFilePath;
// Only pop up document path if the filename is non-empty. We allow whitespace, to
// allow faking a window icon by setting the file path to a single space character.
return !whitespaceRegex.exactMatch(m_cocoaWindow->window()->filePath());
}
- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard
@ -107,6 +112,9 @@
Q_UNUSED(event);
Q_UNUSED(dragImageLocation);
Q_UNUSED(pasteboard);
return m_cocoaWindow && m_cocoaWindow->m_hasWindowFilePath;
// Only allow drag if the filename is non-empty. We allow whitespace, to
// allow faking a window icon by setting the file path to a single space.
return !whitespaceRegex.exactMatch(m_cocoaWindow->window()->filePath());
}
@end