Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcocoafontdialoghelper.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <AppKit/AppKit.h>
5
6#include <QtCore/qtimer.h>
7#include <QtGui/qfontdatabase.h>
8#include <qpa/qplatformtheme.h>
9
10#include <private/qfont_p.h>
11#include <private/qfontengine_p.h>
12#include <private/qfontengine_coretext_p.h>
13
15#include "qcocoahelpers.h"
17
18#if !CGFLOAT_DEFINED
19typedef float CGFloat; // Should only not be defined on 32-bit platforms
20#endif
21
23
24static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont)
25{
26 QFont newFont;
27 if (cocoaFont) {
28 int pSize = qRound([cocoaFont pointSize]);
29 QCFType<CTFontDescriptorRef> font(CTFontCopyFontDescriptor((CTFontRef)cocoaFont));
30 QString family(QCFString((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute)));
31 QString style(QCFString(((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute))));
32
33 newFont = QFontDatabase::font(family, style, pSize);
34 newFont.setUnderline(resolveFont.underline());
35 newFont.setStrikeOut(resolveFont.strikeOut());
36 }
37 return newFont;
38}
39
40@interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject<NSWindowDelegate, QNSPanelDelegate>
43- (void)changeFont:(id)sender;
44- (void)finishOffWithCode:(NSInteger)code;
45@end
46
48
49@implementation QNSFontPanelDelegate {
50 @public
51 NSFontPanel *mFontPanel;
59}
60
61- (instancetype)init
62{
63 if ((self = [super init])) {
64 mFontPanel = [NSFontPanel sharedFontPanel];
65 mHelper = nullptr;
67 mPanelButtons = nil;
68 mResultCode = NSModalResponseCancel;
69 mDialogIsExecuting = false;
70 mResultSet = false;
71
72 [mFontPanel setRestorable:NO];
73 [mFontPanel setDelegate:self];
74
75 [mFontPanel retain];
76 }
77 return self;
78}
79
80- (void)dealloc
81{
82 [mStolenContentView release];
83 [mFontPanel setDelegate:nil];
84 [[NSNotificationCenter defaultCenter] removeObserver:self];
85
86 [super dealloc];
87}
88
89- (void)setDialogHelper:(QCocoaFontDialogHelper *)helper
90{
91 mHelper = helper;
92
93 [mFontPanel setTitle:helper->options()->windowTitle().toNSString()];
94
96 [self restoreOriginalContentView];
97 } else if (!mStolenContentView) {
98 // steal the font panel's contents view
99 mStolenContentView = mFontPanel.contentView;
100 [mStolenContentView retain];
101 mFontPanel.contentView = nil;
102
103 // create a new content view and add the stolen one as a subview
104 mPanelButtons = [[QNSPanelContentsWrapper alloc] initWithPanelDelegate:self];
105 [mPanelButtons addSubview:mStolenContentView];
106 mPanelButtons.panelContentsMargins = NSEdgeInsetsMake(0, 0, 7, 0);
107 mFontPanel.contentView = mPanelButtons;
108 mFontPanel.defaultButtonCell = mPanelButtons.okButton.cell;
109 }
110}
111
112- (void)closePanel
113{
114 [mFontPanel close];
115}
116
117- (void)restoreOriginalContentView
118{
119 if (mStolenContentView) {
120 // return stolen stuff to its rightful owner
121 [mStolenContentView removeFromSuperview];
122 [mFontPanel setContentView:mStolenContentView];
123 mStolenContentView = nil;
124 [mPanelButtons release];
125 mPanelButtons = nil;
126 }
127}
128
129- (void)onOkClicked
130{
131 [mFontPanel close];
132 [self finishOffWithCode:NSModalResponseOK];
133}
134
135- (void)onCancelClicked
136{
137 if (mPanelButtons) {
138 [mFontPanel close];
139 mQtFont = QFont();
140 [self finishOffWithCode:NSModalResponseCancel];
141 }
142}
143
144- (void)changeFont:(id)sender
145{
146 Q_UNUSED(sender);
147 [self updateQtFont];
148}
149
150- (void)updateQtFont
151{
152 // Get selected font
153 NSFontManager *fontManager = [NSFontManager sharedFontManager];
154 NSFont *selectedFont = [fontManager selectedFont];
155 if (!selectedFont) {
156 selectedFont = [NSFont systemFontOfSize:[NSFont systemFontSize]];
157 }
158 NSFont *panelFont = [fontManager convertFont:selectedFont];
159 mQtFont = qfontForCocoaFont(panelFont, mQtFont);
160
161 if (mHelper)
163}
164
165- (void)showModelessPanel
166{
167 mDialogIsExecuting = false;
168 mResultSet = false;
169 // Make this an asynchronous call, so the panel is made key only
170 // in the next event loop run. This is to make sure that by
171 // the time the modal loop is run in runModalForWindow below,
172 // which internally also sets the panel to key window,
173 // the panel is not yet key, and the NSApp still has the right
174 // reference to the _previousKeyWindow. Otherwise both NSApp.key
175 // and NSApp._previousKeyWindow would wrongly point to the panel,
176 // loosing any reference to the window that was key before.
177 dispatch_async(dispatch_get_main_queue(), ^{
178 [mFontPanel makeKeyAndOrderFront:mFontPanel];
179 });
180}
181
182- (BOOL)runApplicationModalPanel
183{
184 mDialogIsExecuting = true;
185 // Call processEvents in case the event dispatcher has been interrupted, and needs to do
186 // cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
187 // close down during the cleanup.
189
190 // Make sure we don't interrupt the runModalForWindow call.
192
193 [NSApp runModalForWindow:mFontPanel];
194 mDialogIsExecuting = false;
195
196 // Wake up the event dispatcher so it can check whether the
197 // current event loop should continue spinning or not.
199
200 return (mResultCode == NSModalResponseOK);
201}
202
203// Future proofing in case _NSTargetForSendAction checks this
204// property before sending us the changeFont: message.
205- (BOOL)worksWhenModal
206{
207 return YES;
208}
209
210- (QPlatformDialogHelper::DialogCode)dialogResultCode
211{
213}
214
215- (BOOL)windowShouldClose:(id)window
216{
218 if (!mPanelButtons)
219 [self updateQtFont];
220 if (mDialogIsExecuting) {
221 [self finishOffWithCode:NSModalResponseCancel];
222 } else {
223 mResultSet = true;
224 if (mHelper)
226 }
227 return true;
228}
229
230- (void)finishOffWithCode:(NSInteger)code
231{
233 if (mDialogIsExecuting) {
234 // We stop the current modal event loop. The control
235 // will then return inside -(void)exec below.
236 // It's important that the modal event loop is stopped before
237 // we accept/reject QFontDialog, since QFontDialog has its
238 // own event loop that needs to be stopped last.
239 [NSApp stopModalWithCode:code];
240 } else {
241 // Since we are not in a modal event loop, we can safely close
242 // down QFontDialog
243 // Calling accept() or reject() can in turn call closeCocoaFontPanel.
244 // This check will prevent any such recursion.
245 if (!mResultSet) {
246 mResultSet = true;
247 if (mResultCode == NSModalResponseCancel) {
249 } else {
251 }
252 }
253 }
254}
255
256@end
257
259
261{
262public:
264 {
265 mDelegate = [[QNSFontPanelDelegate alloc] init];
266 }
267
269 {
270 [mDelegate release];
271 }
272
274 {
275 [mDelegate setDialogHelper:helper];
276 }
277
279 {
280 if (mDelegate->mHelper == helper)
281 mDelegate->mHelper = nullptr;
282 }
283
284 bool exec()
285 {
286 // Note: If NSApp is not running (which is the case if e.g a top-most
287 // QEventLoop has been interrupted, and the second-most event loop has not
288 // yet been reactivated (regardless if [NSApp run] is still on the stack)),
289 // showing a native modal dialog will fail.
290 return [mDelegate runApplicationModalPanel];
291 }
292
293 bool show(Qt::WindowModality windowModality, QWindow *parent)
294 {
295 Q_UNUSED(parent);
296 if (windowModality != Qt::ApplicationModal)
297 [mDelegate showModelessPanel];
298 // no need to show a Qt::ApplicationModal dialog here, because it will be shown in runApplicationModalPanel
299 return true;
300 }
301
302 void hide()
303 {
304 [mDelegate closePanel];
305 }
306
308 {
309 return mDelegate->mQtFont;
310 }
311
313 {
314 NSFontManager *mgr = [NSFontManager sharedFontManager];
315 NSFont *nsFont = nil;
316
317 int weight = 5;
318 NSFontTraitMask mask = 0;
319 if (font.style() == QFont::StyleItalic) {
320 mask |= NSItalicFontMask;
321 }
322 if (font.weight() == QFont::Bold) {
323 weight = 9;
324 mask |= NSBoldFontMask;
325 }
326
327 QFontInfo fontInfo(font);
328 nsFont = [mgr fontWithFamily:fontInfo.family().toNSString()
329 traits:mask
330 weight:weight
331 size:fontInfo.pointSize()];
332
333 [mgr setSelectedFont:nsFont isMultiple:NO];
334 mDelegate->mQtFont = font;
335 }
336
337private:
338 QNSFontPanelDelegate *mDelegate;
339};
340
341Q_GLOBAL_STATIC(QCocoaFontPanel, sharedFontPanel)
342
346
348{
349 sharedFontPanel()->cleanup(this);
350}
351
353{
354 if (sharedFontPanel()->exec())
355 emit accept();
356 else
357 emit reject();
358}
359
360bool QCocoaFontDialogHelper::show(Qt::WindowFlags, Qt::WindowModality windowModality, QWindow *parent)
361{
362 if (windowModality == Qt::ApplicationModal)
363 windowModality = Qt::WindowModal;
364 sharedFontPanel()->init(this);
365 return sharedFontPanel()->show(windowModality, parent);
366}
367
369{
370 sharedFontPanel()->hide();
371}
372
374{
375 sharedFontPanel()->init(this);
376 sharedFontPanel()->setCurrentFont(font);
377}
378
380{
381 return sharedFontPanel()->currentFont();
382}
383
static void clearCurrentThreadCocoaEventDispatcherInterruptFlag()
QFont currentFont() const override
bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override
void setCurrentFont(const QFont &) override
void init(QCocoaFontDialogHelper *helper)
bool show(Qt::WindowModality windowModality, QWindow *parent)
void setCurrentFont(const QFont &font)
void cleanup(QCocoaFontDialogHelper *helper)
static QAbstractEventDispatcher * eventDispatcher()
Returns a pointer to the event dispatcher object for the main thread.
@ ExcludeSocketNotifiers
Definition qeventloop.h:28
@ ExcludeUserInputEvents
Definition qeventloop.h:27
static QFont font(const QString &family, const QString &style, int pointSize)
Returns a QFont object that has family family, style style and point size pointSize.
bool testOption(FontDialogOption option) const
\reentrant
Definition qfontinfo.h:16
\reentrant
Definition qfont.h:22
bool strikeOut() const
Returns true if strikeout has been set; otherwise returns false.
Definition qfont.cpp:1304
bool underline() const
Returns true if underline has been set; otherwise returns false.
Definition qfont.cpp:1251
Weight weight() const
Returns the weight of the font, using the same scale as the \l{QFont::Weight} enumeration.
Definition qfont.cpp:1133
Style style() const
Returns the style of the font.
Definition qfont.cpp:1105
@ Bold
Definition qfont.h:70
@ StyleItalic
Definition qfont.h:78
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
The QPlatformDialogHelper class allows for platform-specific customization of dialogs.
const QSharedPointer< QFontDialogOptions > & options() const
void currentFontChanged(const QFont &font)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtGui
Definition qwindow.h:63
Combined button and popup list for selecting options.
WindowModality
@ WindowModal
@ ApplicationModal
QString self
Definition language.cpp:58
QNSPanelContentsWrapper * mPanelButtons
NSInteger mResultCode
BOOL mResultSet
QCocoaColorDialogHelper * mHelper
BOOL mDialogIsExecuting
NSView * mStolenContentView
QCocoaFontDialogHelper * mHelper
float CGFloat
QNSPanelContentsWrapper * mPanelButtons
QFont mQtFont
NSInteger mResultCode
BOOL mResultSet
BOOL mDialogIsExecuting
static QT_USE_NAMESPACE QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont)
NSView * mStolenContentView
long NSInteger
#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__)
Definition qcore_mac_p.h:58
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
GLuint GLuint GLfloat weight
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define emit
#define Q_UNUSED(x)
aWidget window() -> setWindowTitle("New Window Title")
[2]