Cocoa Dialog Helpers: Refactor OK-Cancel buttons view

Since virtually all the logic is shared between QNSColorPanelDelegate
and QNSFontPanelDelegate, we extract the added buttons and
layouting logic and move it into its own class. This requires
the two afore mentioned Objective C classes to satisfy the
QNSPanelDelegate protocol.

Change-Id: Ie26e758f5db71920896d930a4f3644b51a1ce3fa
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Gabriel de Dietrich 2016-10-24 14:07:33 -07:00 committed by Timur Pocheptsov
parent 1a43199fce
commit 27aeeac6ee
4 changed files with 163 additions and 219 deletions

View File

@ -50,31 +50,13 @@
QT_USE_NAMESPACE QT_USE_NAMESPACE
static NSButton *macCreateButton(const char *text, NSView *superview) @interface QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) : NSObject<NSWindowDelegate, QT_MANGLE_NAMESPACE(QNSPanelDelegate)>
{
static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect];
[button setButtonType:NSMomentaryLightButton];
[button setBezelStyle:NSRoundedBezelStyle];
[button setTitle:(NSString*)(CFStringRef)QCFString(
QPlatformTheme::removeMnemonics(QCoreApplication::translate("QPlatformTheme", text)))];
[[button cell] setFont:[NSFont systemFontOfSize:
[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
[superview addSubview:button];
return button;
}
@class QT_MANGLE_NAMESPACE(QNSColorPanelDelegate);
@interface QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) : NSObject<NSWindowDelegate>
{ {
@public @public
NSColorPanel *mColorPanel; NSColorPanel *mColorPanel;
QCocoaColorDialogHelper *mHelper; QCocoaColorDialogHelper *mHelper;
NSView *mStolenContentView; NSView *mStolenContentView;
NSButton *mOkButton; QNSPanelContentsWrapper *mPanelButtons;
NSButton *mCancelButton;
QColor mQtColor; QColor mQtColor;
NSInteger mResultCode; NSInteger mResultCode;
BOOL mDialogIsExecuting; BOOL mDialogIsExecuting;
@ -82,7 +64,6 @@ static NSButton *macCreateButton(const char *text, NSView *superview)
BOOL mClosingDueToKnownButton; BOOL mClosingDueToKnownButton;
}; };
- (void)restoreOriginalContentView; - (void)restoreOriginalContentView;
- (void)relayout;
- (void)updateQtColor; - (void)updateQtColor;
- (void)finishOffWithCode:(NSInteger)code; - (void)finishOffWithCode:(NSInteger)code;
@end @end
@ -97,8 +78,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
mColorPanel = [NSColorPanel sharedColorPanel]; mColorPanel = [NSColorPanel sharedColorPanel];
mHelper = 0; mHelper = 0;
mStolenContentView = 0; mStolenContentView = 0;
mOkButton = 0; mPanelButtons = nil;
mCancelButton = 0;
mResultCode = NSCancelButton; mResultCode = NSCancelButton;
mDialogIsExecuting = false; mDialogIsExecuting = false;
mResultSet = false; mResultSet = false;
@ -142,23 +122,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
[mColorPanel setContentView:0]; [mColorPanel setContentView:0];
// create a new content view and add the stolen one as a subview // create a new content view and add the stolen one as a subview
NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; mPanelButtons = [[QNSPanelContentsWrapper alloc] initWithPanelDelegate:self];
NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; [mPanelButtons addSubview:mStolenContentView];
[ourContentView addSubview:mStolenContentView]; mColorPanel.contentView = mPanelButtons;
mColorPanel.defaultButtonCell = mPanelButtons.okButton.cell;
// create OK and Cancel buttons and add these as subviews
mOkButton = macCreateButton("&OK", ourContentView);
mCancelButton = macCreateButton("Cancel", ourContentView);
[mColorPanel setContentView:ourContentView];
[mColorPanel setDefaultButtonCell:[mOkButton cell]];
[self relayout];
[mOkButton setAction:@selector(onOkClicked)];
[mOkButton setTarget:self];
[mCancelButton setAction:@selector(onCancelClicked)];
[mCancelButton setTarget:self];
} }
} }
@ -167,12 +134,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
[mColorPanel close]; [mColorPanel close];
} }
- (void)windowDidResize:(NSNotification *)notification
{
Q_UNUSED(notification);
[self relayout];
}
- (void)colorChanged:(NSNotification *)notification - (void)colorChanged:(NSNotification *)notification
{ {
Q_UNUSED(notification); Q_UNUSED(notification);
@ -182,7 +143,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)windowWillClose:(NSNotification *)notification - (void)windowWillClose:(NSNotification *)notification
{ {
Q_UNUSED(notification); Q_UNUSED(notification);
if (mCancelButton && mHelper && !mClosingDueToKnownButton) { if (mPanelButtons && mHelper && !mClosingDueToKnownButton) {
mClosingDueToKnownButton = true; // prevent repeating emit mClosingDueToKnownButton = true; // prevent repeating emit
emit mHelper->reject(); emit mHelper->reject();
} }
@ -191,68 +152,16 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)restoreOriginalContentView - (void)restoreOriginalContentView
{ {
if (mStolenContentView) { if (mStolenContentView) {
NSView *ourContentView = [mColorPanel contentView];
// return stolen stuff to its rightful owner // return stolen stuff to its rightful owner
[mStolenContentView removeFromSuperview]; [mStolenContentView removeFromSuperview];
[mColorPanel setContentView:mStolenContentView]; [mColorPanel setContentView:mStolenContentView];
[mOkButton release]; [mStolenContentView release];
[mCancelButton release]; mStolenContentView = nil;
[ourContentView release]; [mPanelButtons release];
mOkButton = 0; mPanelButtons = nil;
mCancelButton = 0;
mStolenContentView = 0;
} }
} }
- (void)relayout
{
if (!mOkButton)
return;
NSRect rect = [[mStolenContentView superview] frame];
// should a priori be kept in sync with qfontdialog_mac.mm
const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
const CGFloat ButtonMinHeight = 32.0;
const CGFloat ButtonSpacing = 0.0;
const CGFloat ButtonTopMargin = 0.0;
const CGFloat ButtonBottomMargin = 7.0;
const CGFloat ButtonSideMargin = 9.0;
[mOkButton sizeToFit];
NSSize okSizeHint = [mOkButton frame].size;
[mCancelButton sizeToFit];
NSSize cancelSizeHint = [mCancelButton frame].size;
const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth,
qMax(okSizeHint.width, cancelSizeHint.width)),
CGFloat((rect.size.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
const CGFloat ButtonHeight = qMax(ButtonMinHeight,
qMax(okSizeHint.height, cancelSizeHint.height));
NSRect okRect = { { rect.size.width - ButtonSideMargin - ButtonWidth,
ButtonBottomMargin },
{ ButtonWidth, ButtonHeight } };
[mOkButton setFrame:okRect];
[mOkButton setNeedsDisplay:YES];
NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth,
ButtonBottomMargin },
{ ButtonWidth, ButtonHeight } };
[mCancelButton setFrame:cancelRect];
[mCancelButton setNeedsDisplay:YES];
const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin;
NSRect stolenCVRect = { { 0.0, Y },
{ rect.size.width, rect.size.height - Y } };
[mStolenContentView setFrame:stolenCVRect];
[mStolenContentView setNeedsDisplay:YES];
[[mStolenContentView superview] setNeedsDisplay:YES];
}
- (void)onOkClicked - (void)onOkClicked
{ {
mClosingDueToKnownButton = true; mClosingDueToKnownButton = true;
@ -263,7 +172,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)onCancelClicked - (void)onCancelClicked
{ {
if (mOkButton) { if (mPanelButtons) {
mClosingDueToKnownButton = true; mClosingDueToKnownButton = true;
[mColorPanel close]; [mColorPanel close];
mQtColor = QColor(); mQtColor = QColor();
@ -335,7 +244,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (BOOL)windowShouldClose:(id)window - (BOOL)windowShouldClose:(id)window
{ {
Q_UNUSED(window); Q_UNUSED(window);
if (!mOkButton) if (!mPanelButtons)
[self updateQtColor]; [self updateQtColor];
if (mDialogIsExecuting) { if (mDialogIsExecuting) {
[self finishOffWithCode:NSCancelButton]; [self finishOffWithCode:NSCancelButton];

View File

@ -58,33 +58,6 @@ typedef float CGFloat; // Should only not be defined on 32-bit platforms
QT_USE_NAMESPACE QT_USE_NAMESPACE
// should a priori be kept in sync with qcolordialog_mac.mm
const CGFloat ButtonMinWidth = 78.0;
const CGFloat ButtonMinHeight = 32.0;
const CGFloat ButtonSpacing = 0.0;
const CGFloat ButtonTopMargin = 0.0;
const CGFloat ButtonBottomMargin = 7.0;
const CGFloat ButtonSideMargin = 9.0;
// looks better with some margins
const CGFloat DialogTopMargin = 7.0;
const CGFloat DialogSideMargin = 9.0;
static NSButton *macCreateButton(const char *text, NSView *superview)
{
static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect];
[button setButtonType:NSMomentaryLightButton];
[button setBezelStyle:NSRoundedBezelStyle];
[button setTitle:(NSString*)(CFStringRef)QCFString(
QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))];
[[button cell] setFont:[NSFont systemFontOfSize:
[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
[superview addSubview:button];
return button;
}
static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont) static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont)
{ {
QFont newFont; QFont newFont;
@ -103,22 +76,19 @@ static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont)
@class QT_MANGLE_NAMESPACE(QNSFontPanelDelegate); @class QT_MANGLE_NAMESPACE(QNSFontPanelDelegate);
@interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject<NSWindowDelegate> @interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject<NSWindowDelegate, QT_MANGLE_NAMESPACE(QNSPanelDelegate)>
{ {
@public @public
NSFontPanel *mFontPanel; NSFontPanel *mFontPanel;
QCocoaFontDialogHelper *mHelper; QCocoaFontDialogHelper *mHelper;
NSView *mStolenContentView; NSView *mStolenContentView;
NSButton *mOkButton; QNSPanelContentsWrapper *mPanelButtons;
NSButton *mCancelButton;
QFont mQtFont; QFont mQtFont;
NSInteger mResultCode; NSInteger mResultCode;
BOOL mDialogIsExecuting; BOOL mDialogIsExecuting;
BOOL mResultSet; BOOL mResultSet;
}; };
- (void)restoreOriginalContentView; - (void)restoreOriginalContentView;
- (void)relayout;
- (void)relayoutToContentSize:(NSSize)frameSize;
- (void)updateQtFont; - (void)updateQtFont;
- (void)changeFont:(id)sender; - (void)changeFont:(id)sender;
- (void)finishOffWithCode:(NSInteger)code; - (void)finishOffWithCode:(NSInteger)code;
@ -134,8 +104,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
mFontPanel = [NSFontPanel sharedFontPanel]; mFontPanel = [NSFontPanel sharedFontPanel];
mHelper = 0; mHelper = 0;
mStolenContentView = 0; mStolenContentView = 0;
mOkButton = 0; mPanelButtons = 0;
mCancelButton = 0;
mResultCode = NSCancelButton; mResultCode = NSCancelButton;
mDialogIsExecuting = false; mDialogIsExecuting = false;
mResultSet = false; mResultSet = false;
@ -173,23 +142,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
[mFontPanel setContentView:0]; [mFontPanel setContentView:0];
// create a new content view and add the stolen one as a subview // create a new content view and add the stolen one as a subview
NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; mPanelButtons = [[QNSPanelContentsWrapper alloc] initWithPanelDelegate:self];
NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; [mPanelButtons addSubview:mStolenContentView];
[ourContentView addSubview:mStolenContentView]; mPanelButtons.panelContentsMargins = NSEdgeInsetsMake(0, 0, 7, 0);
mFontPanel.contentView = mPanelButtons;
// create OK and Cancel buttons and add these as subviews mFontPanel.defaultButtonCell = mPanelButtons.okButton.cell;
mOkButton = macCreateButton("&OK", ourContentView);
mCancelButton = macCreateButton("Cancel", ourContentView);
[mFontPanel setContentView:ourContentView];
[mFontPanel setDefaultButtonCell:[mOkButton cell]];
[self relayoutToContentSize:[[mStolenContentView superview] frame].size];
[mOkButton setAction:@selector(onOkClicked)];
[mOkButton setTarget:self];
[mCancelButton setAction:@selector(onCancelClicked)];
[mCancelButton setTarget:self];
} }
} }
@ -198,77 +155,18 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
[mFontPanel close]; [mFontPanel close];
} }
- (void)windowDidResize:(NSNotification *)notification
{
Q_UNUSED(notification);
[self relayout];
}
- (void)restoreOriginalContentView - (void)restoreOriginalContentView
{ {
if (mStolenContentView) { if (mStolenContentView) {
NSView *ourContentView = [mFontPanel contentView];
// return stolen stuff to its rightful owner // return stolen stuff to its rightful owner
[mStolenContentView removeFromSuperview]; [mStolenContentView removeFromSuperview];
[mFontPanel setContentView:mStolenContentView]; [mFontPanel setContentView:mStolenContentView];
[mOkButton release];
[mCancelButton release];
[ourContentView release];
mOkButton = 0;
mCancelButton = 0;
mStolenContentView = 0; mStolenContentView = 0;
[mPanelButtons release];
mPanelButtons = nil;
} }
} }
- (void)relayout
{
if (!mOkButton)
return;
[self relayoutToContentSize:[[mStolenContentView superview] frame].size];
}
- (void)relayoutToContentSize:(NSSize)frameSize
{
Q_ASSERT(mOkButton);
[mOkButton sizeToFit];
NSSize okSizeHint = [mOkButton frame].size;
[mCancelButton sizeToFit];
NSSize cancelSizeHint = [mCancelButton frame].size;
const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth,
qMax(okSizeHint.width, cancelSizeHint.width)),
CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
const CGFloat ButtonHeight = qMax(ButtonMinHeight,
qMax(okSizeHint.height, cancelSizeHint.height));
const CGFloat X = DialogSideMargin;
const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin;
NSRect okRect = { { frameSize.width - ButtonSideMargin - ButtonWidth,
ButtonBottomMargin },
{ ButtonWidth, ButtonHeight } };
[mOkButton setFrame:okRect];
[mOkButton setNeedsDisplay:YES];
NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth,
ButtonBottomMargin },
{ ButtonWidth, ButtonHeight } };
[mCancelButton setFrame:cancelRect];
[mCancelButton setNeedsDisplay:YES];
NSRect stolenCVRect = { { X, Y },
{ frameSize.width - X - X, frameSize.height - Y - DialogTopMargin } };
[mStolenContentView setFrame:stolenCVRect];
[mStolenContentView setNeedsDisplay:YES];
[[mStolenContentView superview] setNeedsDisplay:YES];
}
- (void)onOkClicked - (void)onOkClicked
{ {
[mFontPanel close]; [mFontPanel close];
@ -277,7 +175,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
- (void)onCancelClicked - (void)onCancelClicked
{ {
if (mOkButton) { if (mPanelButtons) {
[mFontPanel close]; [mFontPanel close];
mQtFont = QFont(); mQtFont = QFont();
[self finishOffWithCode:NSCancelButton]; [self finishOffWithCode:NSCancelButton];
@ -332,7 +230,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
- (BOOL)windowShouldClose:(id)window - (BOOL)windowShouldClose:(id)window
{ {
Q_UNUSED(window); Q_UNUSED(window);
if (!mOkButton) if (!mPanelButtons)
[self updateQtFont]; [self updateQtFont];
if (mDialogIsExecuting) { if (mDialogIsExecuting) {
[self finishOffWithCode:NSCancelButton]; [self finishOffWithCode:NSCancelButton];

View File

@ -158,7 +158,30 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro
// return default value. // return default value.
return fallback; return fallback;
} }
QT_END_NAMESPACE QT_END_NAMESPACE
@protocol QT_MANGLE_NAMESPACE(QNSPanelDelegate)
@required
- (void)onOkClicked;
- (void)onCancelClicked;
@end
@interface QT_MANGLE_NAMESPACE(QNSPanelContentsWrapper) : NSView
@property (nonatomic, readonly) NSButton *okButton;
@property (nonatomic, readonly) NSButton *cancelButton;
@property (nonatomic, readonly) NSView *panelContents; // ARC: unretained, make it weak
@property (nonatomic, assign) NSEdgeInsets panelContentsMargins;
- (instancetype)initWithPanelDelegate:(id<QT_MANGLE_NAMESPACE(QNSPanelDelegate)>)panelDelegate;
- (void)dealloc;
- (NSButton *)createButtonWithTitle:(const char *)title;
- (void)layout;
@end
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
#endif //QCOCOAHELPERS_H #endif //QCOCOAHELPERS_H

View File

@ -300,3 +300,117 @@ QString qt_mac_removeAmpersandEscapes(QString s)
} }
QT_END_NAMESPACE QT_END_NAMESPACE
/*! \internal
This NSView derived class is used to add OK/Cancel
buttons to NSColorPanel and NSFontPanel. It replaces
the panel's content view, while reparenting the former
content view into itself. It also takes care of setting
the target-action for the OK/Cancel buttons and making
sure the layout is consistent.
*/
@implementation QNSPanelContentsWrapper
- (instancetype)initWithPanelDelegate:(id<QT_MANGLE_NAMESPACE(QNSPanelDelegate)>)panelDelegate
{
if ((self = [super initWithFrame:NSZeroRect])) {
// create OK and Cancel buttons and add these as subviews
_okButton = [self createButtonWithTitle:"&OK"];
_okButton.action = @selector(onOkClicked);
_okButton.target = panelDelegate;
_cancelButton = [self createButtonWithTitle:"Cancel"];
_cancelButton.action = @selector(onCancelClicked);
_cancelButton.target = panelDelegate;
_panelContents = nil;
_panelContentsMargins = NSEdgeInsetsMake(0, 0, 0, 0);
}
return self;
}
- (void)dealloc
{
[_okButton release];
_okButton = nil;
[_cancelButton release];
_cancelButton = nil;
_panelContents = nil;
[super dealloc];
}
- (NSButton *)createButtonWithTitle:(const char *)title
{
NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
button.buttonType = NSMomentaryLightButton;
button.bezelStyle = NSRoundedBezelStyle;
const QString &cleanTitle = QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", title));
// FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy
button.title = (NSString *)cleanTitle.toCFString();
((NSButtonCell *)button.cell).font =
[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
[self addSubview:button];
return button;
}
- (void)layout
{
static const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
static const CGFloat ButtonMinHeight = 32.0;
static const CGFloat ButtonSpacing = 0.0;
static const CGFloat ButtonTopMargin = 0.0;
static const CGFloat ButtonBottomMargin = 7.0;
static const CGFloat ButtonSideMargin = 9.0;
NSSize frameSize = self.frame.size;
[self.okButton sizeToFit];
NSSize okSizeHint = self.okButton.frame.size;
[self.cancelButton sizeToFit];
NSSize cancelSizeHint = self.cancelButton.frame.size;
const CGFloat buttonWidth = qMin(qMax(ButtonMinWidth,
qMax(okSizeHint.width, cancelSizeHint.width)),
CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
const CGFloat buttonHeight = qMax(ButtonMinHeight,
qMax(okSizeHint.height, cancelSizeHint.height));
NSRect okRect = { { frameSize.width - ButtonSideMargin - buttonWidth,
ButtonBottomMargin },
{ buttonWidth, buttonHeight } };
self.okButton.frame = okRect;
self.okButton.needsDisplay = YES;
NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - buttonWidth,
ButtonBottomMargin },
{ buttonWidth, buttonHeight } };
self.cancelButton.frame = cancelRect;
self.cancelButton.needsDisplay = YES;
// The third view should be the original panel contents. Cache it.
if (!self.panelContents)
for (NSView *view in self.subviews)
if (view != self.okButton && view != self.cancelButton) {
_panelContents = view;
break;
}
const CGFloat buttonBoxHeight = ButtonBottomMargin + buttonHeight + ButtonTopMargin;
const NSRect panelContentsFrame = NSMakeRect(
self.panelContentsMargins.left,
buttonBoxHeight + self.panelContentsMargins.bottom,
frameSize.width - (self.panelContentsMargins.left + self.panelContentsMargins.right),
frameSize.height - buttonBoxHeight - (self.panelContentsMargins.top + self.panelContentsMargins.bottom));
self.panelContents.frame = panelContentsFrame;
self.panelContents.needsDisplay = YES;
self.needsDisplay = YES;
}
@end