/
message_box_mac.mm
156 lines (127 loc) · 5.32 KB
/
message_box_mac.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/ui/message_box.h"
#include <string>
#include <utility>
#include <vector>
#import <Cocoa/Cocoa.h>
#include "base/callback.h"
#include "base/mac/mac_util.h"
#include "base/strings/sys_string_conversions.h"
#include "shell/browser/native_window.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/gfx/image/image_skia.h"
namespace electron {
MessageBoxSettings::MessageBoxSettings() = default;
MessageBoxSettings::MessageBoxSettings(const MessageBoxSettings&) = default;
MessageBoxSettings::~MessageBoxSettings() = default;
namespace {
NSAlert* CreateNSAlert(const MessageBoxSettings& settings) {
// Ignore the title; it's the window title on other platforms and ignorable.
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:base::SysUTF8ToNSString(settings.message)];
[alert setInformativeText:base::SysUTF8ToNSString(settings.detail)];
switch (settings.type) {
case MessageBoxType::kInformation:
alert.alertStyle = NSInformationalAlertStyle;
break;
case MessageBoxType::kWarning:
case MessageBoxType::kError:
// NSWarningAlertStyle shows the app icon while NSCriticalAlertStyle
// shows a warning icon with an app icon badge. Since there is no
// error variant, lets just use NSCriticalAlertStyle.
alert.alertStyle = NSCriticalAlertStyle;
break;
default:
break;
}
for (size_t i = 0; i < settings.buttons.size(); ++i) {
NSString* title = base::SysUTF8ToNSString(settings.buttons[i]);
// An empty title causes crash on macOS.
if (settings.buttons[i].empty())
title = @"(empty)";
NSButton* button = [alert addButtonWithTitle:title];
[button setTag:i];
}
NSArray* ns_buttons = [alert buttons];
int button_count = static_cast<int>([ns_buttons count]);
if (settings.default_id >= 0 && settings.default_id < button_count) {
// Highlight the button at default_id
[[ns_buttons objectAtIndex:settings.default_id] highlight:YES];
// The first button added gets set as the default selected, so remove
// that and set the button @ default_id to be default.
[[ns_buttons objectAtIndex:0] setKeyEquivalent:@""];
[[ns_buttons objectAtIndex:settings.default_id] setKeyEquivalent:@"\r"];
}
// Bind cancel id button to escape key if there is more than one button
if (button_count > 1 && settings.cancel_id >= 0 &&
settings.cancel_id < button_count) {
[[ns_buttons objectAtIndex:settings.cancel_id] setKeyEquivalent:@"\e"];
}
if (!settings.checkbox_label.empty()) {
alert.showsSuppressionButton = YES;
alert.suppressionButton.title =
base::SysUTF8ToNSString(settings.checkbox_label);
alert.suppressionButton.state =
settings.checkbox_checked ? NSOnState : NSOffState;
}
if (!settings.icon.isNull()) {
NSImage* image = skia::SkBitmapToNSImageWithColorSpace(
*settings.icon.bitmap(), base::mac::GetGenericRGBColorSpace());
[alert setIcon:image];
}
return alert;
}
} // namespace
int ShowMessageBoxSync(const MessageBoxSettings& settings) {
NSAlert* alert = CreateNSAlert(settings);
// Use runModal for synchronous alert without parent, since we don't have a
// window to wait for. Also use it when window is provided but it is not
// shown as it would be impossible to dismiss the alert if it is connected
// to invisible window (see #22671).
if (!settings.parent_window || !settings.parent_window->IsVisible())
return [[alert autorelease] runModal];
__block int ret_code = -1;
NSWindow* window =
settings.parent_window->GetNativeWindow().GetNativeNSWindow();
[alert beginSheetModalForWindow:window
completionHandler:^(NSModalResponse response) {
ret_code = response;
[NSApp stopModal];
}];
[NSApp runModalForWindow:window];
return ret_code;
}
void ShowMessageBox(const MessageBoxSettings& settings,
MessageBoxCallback callback) {
NSAlert* alert = CreateNSAlert(settings);
// Use runModal for synchronous alert without parent, since we don't have a
// window to wait for.
if (!settings.parent_window) {
int ret = [[alert autorelease] runModal];
std::move(callback).Run(ret, alert.suppressionButton.state == NSOnState);
} else {
NSWindow* window =
settings.parent_window
? settings.parent_window->GetNativeWindow().GetNativeNSWindow()
: nil;
// Duplicate the callback object here since c is a reference and gcd would
// only store the pointer, by duplication we can force gcd to store a copy.
__block MessageBoxCallback callback_ = std::move(callback);
[alert beginSheetModalForWindow:window
completionHandler:^(NSModalResponse response) {
std::move(callback_).Run(
response, alert.suppressionButton.state == NSOnState);
}];
}
}
void ShowErrorBox(const base::string16& title, const base::string16& content) {
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:base::SysUTF16ToNSString(title)];
[alert setInformativeText:base::SysUTF16ToNSString(content)];
[alert setAlertStyle:NSCriticalAlertStyle];
[alert runModal];
[alert release];
}
} // namespace electron