Dark mode implemented
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1A14FC2324D203D9009B3F83 /* TitlebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A14FC2224D203D9009B3F83 /* TitlebarView.swift */; };
|
||||
1A14FC2624D251BD009B3F83 /* darkmode.css in Resources */ = {isa = PBXBuildFile; fileRef = 1A14FC2524D251BD009B3F83 /* darkmode.css */; };
|
||||
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */; };
|
||||
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */; };
|
||||
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ADFF46824C7DE54006DC7AE /* Assets.xcassets */; };
|
||||
@@ -54,6 +55,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1A14FC2224D203D9009B3F83 /* TitlebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitlebarView.swift; sourceTree = "<group>"; };
|
||||
1A14FC2524D251BD009B3F83 /* darkmode.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; path = darkmode.css; sourceTree = "<group>"; };
|
||||
1ADFF45C24C7DE53006DC7AE /* SBrowser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SBrowser.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
@@ -102,6 +104,14 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1A14FC2424D2517A009B3F83 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1A14FC2524D251BD009B3F83 /* darkmode.css */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1ADFF45324C7DE53006DC7AE = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -131,6 +141,7 @@
|
||||
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
|
||||
1ADFF4C124CA6AE4006DC7AE /* Utilities */,
|
||||
1ADFF4AF24C92E2F006DC7AE /* Web Process Bundle Bridge */,
|
||||
1A14FC2424D2517A009B3F83 /* Resources */,
|
||||
1ADFF47624C7DF7F006DC7AE /* Supporting Files */,
|
||||
);
|
||||
path = SBrowser;
|
||||
@@ -297,6 +308,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1A14FC2624D251BD009B3F83 /* darkmode.css in Resources */,
|
||||
1ADFF46C24C7DE54006DC7AE /* LaunchScreen.storyboard in Resources */,
|
||||
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */,
|
||||
);
|
||||
|
||||
@@ -65,6 +65,12 @@ class BrowserViewController: UIViewController,
|
||||
self.present(navController, animated: true, completion: nil)
|
||||
}), for: .touchUpInside)
|
||||
|
||||
// Dark mode button
|
||||
toolbarController.darkModeButton.addAction(UIAction(handler: { _ in
|
||||
self.bridge.darkModeEnabled = !self.bridge.darkModeEnabled
|
||||
self.toolbarController.darkModeEnabled = self.bridge.darkModeEnabled
|
||||
}), for: .touchUpInside)
|
||||
|
||||
// TextField delegate
|
||||
toolbarController.urlBar.textField.delegate = self
|
||||
|
||||
|
||||
@@ -116,12 +116,25 @@ class ToolbarViewController: UIViewController
|
||||
let shareButton = UIButton(frame: .zero)
|
||||
let darkModeButton = UIButton(frame: .zero)
|
||||
|
||||
var darkModeEnabled: Bool = false {
|
||||
didSet {
|
||||
if darkModeEnabled {
|
||||
darkModeButton.setImage(darkModeEnabledImage, for: .normal)
|
||||
} else {
|
||||
darkModeButton.setImage(darkModeDisabledImage, for: .normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let darkModeDisabledImage = UIImage(systemName: "moon.circle")
|
||||
private let darkModeEnabledImage = UIImage(systemName: "moon.circle.fill")
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
toolbarView.urlBar = urlBar
|
||||
|
||||
darkModeButton.setImage(UIImage(systemName: "moon.circle"), for: .normal)
|
||||
darkModeButton.setImage(darkModeDisabledImage, for: .normal)
|
||||
toolbarView.buttonsView.addButtonView(darkModeButton)
|
||||
|
||||
shareButton.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal)
|
||||
|
||||
@@ -23,17 +23,20 @@ class URLBar: UIView
|
||||
|
||||
private let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
|
||||
private let progressIndicatorView = ProgressIndicatorView()
|
||||
private let fadeMaskView = UIImageView(frame: .zero)
|
||||
|
||||
private let refreshImage = UIImage(systemName: "arrow.clockwise")
|
||||
private let stopImage = UIImage(systemName: "xmark")
|
||||
|
||||
private let backgroundCornerRadius: CGFloat = 8
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: .zero)
|
||||
|
||||
backgroundColor = .clear
|
||||
|
||||
backgroundView.layer.masksToBounds = true
|
||||
backgroundView.layer.cornerRadius = 8
|
||||
backgroundView.layer.cornerRadius = backgroundCornerRadius
|
||||
backgroundView.layer.borderWidth = 1
|
||||
backgroundView.layer.borderColor = UIColor.systemFill.cgColor
|
||||
backgroundView.isUserInteractionEnabled = false
|
||||
@@ -85,12 +88,47 @@ class URLBar: UIView
|
||||
return CGSize(width: 1000.0, height: preferredHeight)
|
||||
}
|
||||
|
||||
private func fadeBackgroundImageForSize(_ size: CGSize) -> UIImage? {
|
||||
var image: UIImage? = nil
|
||||
|
||||
UIGraphicsBeginImageContext(CGSize(width: size.width, height: 1.0))
|
||||
if let context = UIGraphicsGetCurrentContext() {
|
||||
let gradientColorsArray = [
|
||||
UIColor(white: 1.0, alpha: 1.0).cgColor,
|
||||
UIColor(white: 1.0, alpha: 1.0).cgColor,
|
||||
UIColor(white: 1.0, alpha: 0.08).cgColor,
|
||||
UIColor(white: 1.0, alpha: 0.08).cgColor
|
||||
]
|
||||
|
||||
let locations: [CGFloat] = [
|
||||
0.0, 0.80, 0.90, 1.0
|
||||
]
|
||||
|
||||
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColorsArray as CFArray, locations: locations) {
|
||||
context.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
|
||||
}
|
||||
|
||||
image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
backgroundView.frame = bounds
|
||||
progressIndicatorView.frame = backgroundView.contentView.bounds
|
||||
textField.frame = bounds.insetBy(dx: 6.0, dy: 0)
|
||||
|
||||
fadeMaskView.frame = textField.bounds
|
||||
fadeMaskView.image = fadeBackgroundImageForSize(fadeMaskView.frame.size)
|
||||
if !textField.isFirstResponder {
|
||||
textField.mask = fadeMaskView
|
||||
} else {
|
||||
textField.mask = nil
|
||||
}
|
||||
|
||||
let refreshButtonSize = CGSize(width: textField.frame.height, height: textField.frame.height)
|
||||
refreshButton.frame = CGRect(origin: CGPoint(x: bounds.width - refreshButtonSize.width, y: 0), size: refreshButtonSize)
|
||||
}
|
||||
|
||||
65
SBrowser/Resources/darkmode.css
Normal file
65
SBrowser/Resources/darkmode.css
Normal file
@@ -0,0 +1,65 @@
|
||||
html, body {
|
||||
color: #555 !important;
|
||||
background: #ececec !important;
|
||||
}
|
||||
|
||||
html, iframe {
|
||||
filter: invert(100%) !important;
|
||||
-webkit-filter: invert(100%) !important;
|
||||
}
|
||||
|
||||
em,
|
||||
img,
|
||||
svg,
|
||||
form,
|
||||
image,
|
||||
video,
|
||||
audio,
|
||||
embed,
|
||||
object,
|
||||
button,
|
||||
canvas,
|
||||
figure:empty {
|
||||
opacity: 0.85;
|
||||
filter: invert(100%) !important;
|
||||
-webkit-filter: invert(100%) !important;
|
||||
}
|
||||
|
||||
form em,
|
||||
form img,
|
||||
form svg,
|
||||
form image,
|
||||
form video,
|
||||
form embed,
|
||||
form object,
|
||||
form button,
|
||||
form canvas,
|
||||
form figure:empty {
|
||||
filter: invert(0) !important;
|
||||
-webkit-filter: invert(0) !important;
|
||||
}
|
||||
|
||||
[style*='background:url']:not(html):not(body):not(input),
|
||||
[style*='background: url']:not(html):not(body):not(input),
|
||||
[style*='background-image']:not(html):not(body):not(input) {
|
||||
opacity: 0.8;
|
||||
filter: invert(100%) !important;
|
||||
-webkit-filter: invert(100%) !important;
|
||||
}
|
||||
|
||||
::-moz-scrollbar {background: #28292a !important}
|
||||
::-webkit-scrollbar {background: #28292a !important}
|
||||
::-moz-scrollbar-track {background: #343637 !important}
|
||||
::-webkit-scrollbar-track {background: #343637 !important}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #4d4e4f !important;
|
||||
border-left: 1px solid #343637 !important;
|
||||
border-right: 1px solid #343637 !important;
|
||||
}
|
||||
|
||||
::-moz-scrollbar-thumb {
|
||||
background: #4d4e4f !important;
|
||||
border-left: 1px solid #343637 !important;
|
||||
border-right: 1px solid #343637 !important;
|
||||
}
|
||||
@@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, weak) id<SBRProcessBundleBridgeDelegate> delegate;
|
||||
@property (nonatomic, strong) id<SBRResourceOriginPolicyDataSource> policyDataSource;
|
||||
@property (nonatomic, assign) BOOL allowAllScripts; // default is NO
|
||||
@property (nonatomic, assign) BOOL darkModeEnabled;
|
||||
|
||||
- (void)policyDataSourceDidChange;
|
||||
|
||||
|
||||
@@ -13,18 +13,23 @@
|
||||
#import <WebKit/_WKRemoteObjectInterface.h>
|
||||
#import <WebKit/_WKRemoteObjectRegistry.h>
|
||||
#import <WebKit/_WKProcessPoolConfiguration.h>
|
||||
#import <WebKit/_WKUserStyleSheet.h>
|
||||
|
||||
#import <WebKit/WKProcessPoolPrivate.h>
|
||||
#import <WebKit/WKWebViewPrivate.h>
|
||||
#import <WebKit/WKWebViewConfigurationPrivate.h>
|
||||
#import <WebKit/WKUserContentControllerPrivate.h>
|
||||
|
||||
@interface SBRProcessBundleBridge () <SBRWebProcessDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation SBRProcessBundleBridge {
|
||||
WKWebView *_webView;
|
||||
WKWebView *_webView;
|
||||
WKWebViewConfiguration *_webViewConfiguration;
|
||||
id<SBRWebProcessProxy> _webProcessProxy;
|
||||
|
||||
_WKUserStyleSheet *_darkModeStyleSheet;
|
||||
}
|
||||
|
||||
- (WKWebView *)webView
|
||||
@@ -58,6 +63,7 @@
|
||||
[[webView _remoteObjectRegistry] registerExportedObject:self interface:delegateInterface];
|
||||
|
||||
_webView = webView;
|
||||
_webViewConfiguration = configuration;
|
||||
}
|
||||
|
||||
return _webView;
|
||||
@@ -90,4 +96,23 @@
|
||||
[_webProcessProxy setAllScriptsAllowed:allowAllScripts];
|
||||
}
|
||||
|
||||
- (void)setDarkModeEnabled:(BOOL)darkModeEnabled
|
||||
{
|
||||
_darkModeEnabled = darkModeEnabled;
|
||||
|
||||
WKUserContentController *userContentController = [_webViewConfiguration userContentController];
|
||||
|
||||
if (darkModeEnabled) {
|
||||
if (!_darkModeStyleSheet) {
|
||||
NSURL *styleSheetURL = [[NSBundle mainBundle] URLForResource:@"darkmode" withExtension:@"css"];
|
||||
NSString *styleSheetSource = [NSString stringWithContentsOfURL:styleSheetURL encoding:NSUTF8StringEncoding error:nil];
|
||||
_darkModeStyleSheet = [[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO];
|
||||
}
|
||||
|
||||
[userContentController _addUserStyleSheet:_darkModeStyleSheet];
|
||||
} else if (_darkModeStyleSheet) {
|
||||
[userContentController _removeUserStyleSheet:_darkModeStyleSheet];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user