Skip to content

Commit cef386b

Browse files
committed
Add support for Dark Mode in MySQL Help Viewer (10.14)
1 parent b536850 commit cef386b

File tree

4 files changed

+118
-11
lines changed

4 files changed

+118
-11
lines changed

Resources/Templates/SPMySQLHelpTemplate.html

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@
1111
font-size:9pt;
1212
}
1313

14-
h2 {}
15-
16-
ul {}
17-
18-
li {}
19-
2014
.internallink {
2115
color: #6A81DD;
2216
text-decoration: none;
2317
}
18+
19+
.internallink:hover {
20+
text-decoration: underline;
21+
}
2422

2523
.description {
2624
font-family: Monaco, monospace;
@@ -42,9 +40,23 @@
4240
color: #ff415a;
4341
font-family: monospace;
4442
}
43+
44+
body.dark {
45+
background-color: rgb(42, 38, 38);
46+
color: white;
47+
}
48+
49+
.dark .internallink {
50+
color: rgb(65, 156, 255);
51+
}
4552
</style>
53+
<script type="text/javascript">
54+
window.onThemeChange = function(theme) {
55+
document.body.className = (theme === 'dark') ? 'dark' : '';
56+
};
57+
</script>
4658
</head>
47-
<body>
59+
<body class="{{bodyClass}}">
4860
{{body}}
4961
</body>
5062
</html>

Source/SPCompatibility.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@
6262

6363
@end
6464

65+
// This is actually a @protocol
66+
@interface NSWindow (Mavericks)
67+
68+
// actual return type is `NSAppearance *`
69+
@property (readonly) id effectiveAppearance;
70+
71+
@end
72+
73+
#define NSAppearanceNameAqua @"NSAppearanceNameAqua"
74+
6575
#endif
6676

6777
#pragma mark - 10.10 Yosemite
@@ -157,3 +167,32 @@ typedef struct {
157167
#endif
158168

159169
#endif
170+
171+
#pragma mark - 10.13 High Sierra
172+
173+
#ifndef __MAC_10_13
174+
#define __MAC_10_13 101300
175+
#endif
176+
177+
#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13
178+
179+
#endif
180+
181+
#pragma mark - 10.14 Mojave
182+
183+
#ifndef __MAC_10_14
184+
#define __MAC_10_14 101400
185+
#endif
186+
187+
#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_14
188+
189+
// NSAppearance class is supported since 10.9, but this file has to go back to 10.8
190+
@interface NSObject (NSAppearance_Mojave)
191+
192+
- (NSString *)bestMatchFromAppearancesWithNames:(NSArray *)appearances;
193+
194+
@end
195+
196+
#define NSAppearanceNameDarkAqua @"NSAppearanceNameDarkAqua"
197+
198+
#endif

Source/SPHelpViewerClient.m

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
#import "RegexKitLite.h"
3636
#import "MGTemplateEngine.h"
3737
#import "ICUTemplateMatcher.h"
38+
#import "SPOSInfo.h"
39+
40+
static BOOL isOSAtLeast10_14 = NO;
3841

3942
@interface SPHelpViewerClient () <SPHelpViewerDataSource>
4043

@@ -46,6 +49,11 @@ - (void)helpViewerClosed:(NSNotification *)notification;
4649

4750
@implementation SPHelpViewerClient
4851

52+
+ (void)initialize
53+
{
54+
isOSAtLeast10_14 = [SPOSInfo isOSVersionAtLeastMajor:10 minor:14 patch:0];
55+
}
56+
4957
- (instancetype)init
5058
{
5159
if (self = [super init]) {
@@ -220,10 +228,23 @@ - (NSString *)HTMLHelpContentsForSearchString:(NSString *)searchString autoHelp:
220228
[tableDetails release];
221229

222230
generate_help:
223-
return [engine processTemplate:helpHTMLTemplate withVariables:@{
224-
@"title": theTitle,
225-
@"body": theHelp,
226-
}];
231+
{ // C syntax disallows a new variable directly following a label…
232+
NSString *addBodyClass = @"";
233+
// Add CSS class if running in dark UI mode (10.14+)
234+
if (isOSAtLeast10_14) {
235+
NSString *match = [[[controller window] effectiveAppearance] bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
236+
// aqua is already the default theme
237+
if ([NSAppearanceNameDarkAqua isEqualToString:match]) {
238+
addBodyClass = @"dark";
239+
}
240+
}
241+
242+
return [engine processTemplate:helpHTMLTemplate withVariables:@{
243+
@"bodyClass": addBodyClass,
244+
@"title": theTitle,
245+
@"body": theHelp,
246+
}];
247+
}
227248
}
228249

229250
+ (NSString *)linkToHelpTopic:(NSString *)aTopic

Source/SPHelpViewerController.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#import "SPHelpViewerController.h"
3333

34+
#import "SPOSInfo.h"
3435
#import <WebKit/WebKit.h>
3536

3637
NSString * const SPHelpViewerSearchTOC = @"contents";
@@ -44,6 +45,7 @@ typedef NS_ENUM(NSInteger, HelpNavButton) {
4445
};
4546

4647
static void *HelpViewerControllerKVOContext = &HelpViewerControllerKVOContext;
48+
static BOOL isOSAtLeast10_14 = NO;
4749

4850
@interface SPHelpViewerController () <WebPolicyDelegate, WebUIDelegate, NSWindowDelegate>
4951
- (IBAction)showHelpForSearchString:(id)sender;
@@ -58,6 +60,7 @@ - (IBAction)helpSelectHelpTargetWeb:(id)sender;
5860
- (IBAction)showHelpForWebViewSelection:(id)sender;
5961
- (IBAction)searchInDocForWebViewSelection:(id)sender;
6062
- (void)helpTargetValidation;
63+
- (void)themeChanged;
6164
- (void)updateWindowTitle;
6265
@end
6366

@@ -67,6 +70,11 @@ @implementation SPHelpViewerController
6770

6871
@synthesize dataSource = dataSource;
6972

73+
+ (void)initialize
74+
{
75+
isOSAtLeast10_14 = [SPOSInfo isOSVersionAtLeastMajor:10 minor:14 patch:0];
76+
}
77+
7078
- (instancetype)init
7179
{
7280
if ((self = [super initWithWindowNibName:@"HelpViewer"])) {
@@ -79,6 +87,9 @@ - (instancetype)init
7987
- (void)dealloc
8088
{
8189
[helpWebView removeObserver:self forKeyPath:@"mainFrameTitle"]; //TODO: update to ...context: variant after 10.6
90+
if(isOSAtLeast10_14) {
91+
[[self window] removeObserver:self forKeyPath:@"effectiveAppearance" context:HelpViewerControllerKVOContext];
92+
}
8293
[super dealloc];
8394
}
8495

@@ -91,6 +102,9 @@ - (void)windowDidLoad
91102
[self updateWindowTitle];
92103

93104
[helpWebView addObserver:self forKeyPath:@"mainFrameTitle" options:0 context:HelpViewerControllerKVOContext];
105+
if(isOSAtLeast10_14) {
106+
[[self window] addObserver:self forKeyPath:@"effectiveAppearance" options:0 context:HelpViewerControllerKVOContext];
107+
}
94108
}
95109

96110
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context
@@ -99,12 +113,33 @@ - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable i
99113
if([@"mainFrameTitle" isEqualToString:keyPath] && object == helpWebView) {
100114
[self updateWindowTitle];
101115
}
116+
else if([@"effectiveAppearance" isEqualToString:keyPath]) {
117+
// Apple says to not do stuff here that could take some time or it may interrupt animations
118+
[self performSelector:@selector(themeChanged) withObject:nil afterDelay:0.0];
119+
}
102120
}
103121
else {
104122
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
105123
}
106124
}
107125

126+
- (void)themeChanged
127+
{
128+
if(!isOSAtLeast10_14) return;
129+
130+
NSString *match = [[[self window] effectiveAppearance] bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
131+
NSString *newTheme = @"unknown";
132+
if([NSAppearanceNameAqua isEqualToString:match]) {
133+
newTheme = @"light";
134+
}
135+
else if([NSAppearanceNameDarkAqua isEqualToString:match]) {
136+
newTheme = @"dark";
137+
}
138+
139+
NSString *eval = [NSString stringWithFormat:@"window.onThemeChange('%@')", newTheme];
140+
[helpWebView stringByEvaluatingJavaScriptFromString:eval];
141+
}
142+
108143
- (void)updateWindowTitle
109144
{
110145
NSString *title = NSLocalizedString(@"MySQL Help", @"mysql help");

0 commit comments

Comments
 (0)