Dark mode implemented
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1A14FC2324D203D9009B3F83 /* TitlebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A14FC2224D203D9009B3F83 /* TitlebarView.swift */; };
|
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 */; };
|
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */; };
|
||||||
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */; };
|
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */; };
|
||||||
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ADFF46824C7DE54006DC7AE /* Assets.xcassets */; };
|
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ADFF46824C7DE54006DC7AE /* Assets.xcassets */; };
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1A14FC2224D203D9009B3F83 /* TitlebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitlebarView.swift; sourceTree = "<group>"; };
|
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; };
|
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>"; };
|
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>"; };
|
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
@@ -102,6 +104,14 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
1A14FC2424D2517A009B3F83 /* Resources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
1A14FC2524D251BD009B3F83 /* darkmode.css */,
|
||||||
|
);
|
||||||
|
path = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
1ADFF45324C7DE53006DC7AE = {
|
1ADFF45324C7DE53006DC7AE = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -131,6 +141,7 @@
|
|||||||
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
|
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
|
||||||
1ADFF4C124CA6AE4006DC7AE /* Utilities */,
|
1ADFF4C124CA6AE4006DC7AE /* Utilities */,
|
||||||
1ADFF4AF24C92E2F006DC7AE /* Web Process Bundle Bridge */,
|
1ADFF4AF24C92E2F006DC7AE /* Web Process Bundle Bridge */,
|
||||||
|
1A14FC2424D2517A009B3F83 /* Resources */,
|
||||||
1ADFF47624C7DF7F006DC7AE /* Supporting Files */,
|
1ADFF47624C7DF7F006DC7AE /* Supporting Files */,
|
||||||
);
|
);
|
||||||
path = SBrowser;
|
path = SBrowser;
|
||||||
@@ -297,6 +308,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
1A14FC2624D251BD009B3F83 /* darkmode.css in Resources */,
|
||||||
1ADFF46C24C7DE54006DC7AE /* LaunchScreen.storyboard in Resources */,
|
1ADFF46C24C7DE54006DC7AE /* LaunchScreen.storyboard in Resources */,
|
||||||
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */,
|
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -65,6 +65,12 @@ class BrowserViewController: UIViewController,
|
|||||||
self.present(navController, animated: true, completion: nil)
|
self.present(navController, animated: true, completion: nil)
|
||||||
}), for: .touchUpInside)
|
}), 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
|
// TextField delegate
|
||||||
toolbarController.urlBar.textField.delegate = self
|
toolbarController.urlBar.textField.delegate = self
|
||||||
|
|
||||||
|
|||||||
@@ -116,12 +116,25 @@ class ToolbarViewController: UIViewController
|
|||||||
let shareButton = UIButton(frame: .zero)
|
let shareButton = UIButton(frame: .zero)
|
||||||
let darkModeButton = 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() {
|
init() {
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
toolbarView.urlBar = urlBar
|
toolbarView.urlBar = urlBar
|
||||||
|
|
||||||
darkModeButton.setImage(UIImage(systemName: "moon.circle"), for: .normal)
|
darkModeButton.setImage(darkModeDisabledImage, for: .normal)
|
||||||
toolbarView.buttonsView.addButtonView(darkModeButton)
|
toolbarView.buttonsView.addButtonView(darkModeButton)
|
||||||
|
|
||||||
shareButton.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal)
|
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 backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
|
||||||
private let progressIndicatorView = ProgressIndicatorView()
|
private let progressIndicatorView = ProgressIndicatorView()
|
||||||
|
private let fadeMaskView = UIImageView(frame: .zero)
|
||||||
|
|
||||||
private let refreshImage = UIImage(systemName: "arrow.clockwise")
|
private let refreshImage = UIImage(systemName: "arrow.clockwise")
|
||||||
private let stopImage = UIImage(systemName: "xmark")
|
private let stopImage = UIImage(systemName: "xmark")
|
||||||
|
|
||||||
|
private let backgroundCornerRadius: CGFloat = 8
|
||||||
|
|
||||||
convenience init() {
|
convenience init() {
|
||||||
self.init(frame: .zero)
|
self.init(frame: .zero)
|
||||||
|
|
||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
|
|
||||||
backgroundView.layer.masksToBounds = true
|
backgroundView.layer.masksToBounds = true
|
||||||
backgroundView.layer.cornerRadius = 8
|
backgroundView.layer.cornerRadius = backgroundCornerRadius
|
||||||
backgroundView.layer.borderWidth = 1
|
backgroundView.layer.borderWidth = 1
|
||||||
backgroundView.layer.borderColor = UIColor.systemFill.cgColor
|
backgroundView.layer.borderColor = UIColor.systemFill.cgColor
|
||||||
backgroundView.isUserInteractionEnabled = false
|
backgroundView.isUserInteractionEnabled = false
|
||||||
@@ -85,12 +88,47 @@ class URLBar: UIView
|
|||||||
return CGSize(width: 1000.0, height: preferredHeight)
|
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() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
backgroundView.frame = bounds
|
backgroundView.frame = bounds
|
||||||
progressIndicatorView.frame = backgroundView.contentView.bounds
|
progressIndicatorView.frame = backgroundView.contentView.bounds
|
||||||
textField.frame = bounds.insetBy(dx: 6.0, dy: 0)
|
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)
|
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)
|
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, weak) id<SBRProcessBundleBridgeDelegate> delegate;
|
||||||
@property (nonatomic, strong) id<SBRResourceOriginPolicyDataSource> policyDataSource;
|
@property (nonatomic, strong) id<SBRResourceOriginPolicyDataSource> policyDataSource;
|
||||||
@property (nonatomic, assign) BOOL allowAllScripts; // default is NO
|
@property (nonatomic, assign) BOOL allowAllScripts; // default is NO
|
||||||
|
@property (nonatomic, assign) BOOL darkModeEnabled;
|
||||||
|
|
||||||
- (void)policyDataSourceDidChange;
|
- (void)policyDataSourceDidChange;
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,12 @@
|
|||||||
#import <WebKit/_WKRemoteObjectInterface.h>
|
#import <WebKit/_WKRemoteObjectInterface.h>
|
||||||
#import <WebKit/_WKRemoteObjectRegistry.h>
|
#import <WebKit/_WKRemoteObjectRegistry.h>
|
||||||
#import <WebKit/_WKProcessPoolConfiguration.h>
|
#import <WebKit/_WKProcessPoolConfiguration.h>
|
||||||
|
#import <WebKit/_WKUserStyleSheet.h>
|
||||||
|
|
||||||
#import <WebKit/WKProcessPoolPrivate.h>
|
#import <WebKit/WKProcessPoolPrivate.h>
|
||||||
#import <WebKit/WKWebViewPrivate.h>
|
#import <WebKit/WKWebViewPrivate.h>
|
||||||
#import <WebKit/WKWebViewConfigurationPrivate.h>
|
#import <WebKit/WKWebViewConfigurationPrivate.h>
|
||||||
|
#import <WebKit/WKUserContentControllerPrivate.h>
|
||||||
|
|
||||||
@interface SBRProcessBundleBridge () <SBRWebProcessDelegate>
|
@interface SBRProcessBundleBridge () <SBRWebProcessDelegate>
|
||||||
|
|
||||||
@@ -24,7 +26,10 @@
|
|||||||
|
|
||||||
@implementation SBRProcessBundleBridge {
|
@implementation SBRProcessBundleBridge {
|
||||||
WKWebView *_webView;
|
WKWebView *_webView;
|
||||||
|
WKWebViewConfiguration *_webViewConfiguration;
|
||||||
id<SBRWebProcessProxy> _webProcessProxy;
|
id<SBRWebProcessProxy> _webProcessProxy;
|
||||||
|
|
||||||
|
_WKUserStyleSheet *_darkModeStyleSheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (WKWebView *)webView
|
- (WKWebView *)webView
|
||||||
@@ -58,6 +63,7 @@
|
|||||||
[[webView _remoteObjectRegistry] registerExportedObject:self interface:delegateInterface];
|
[[webView _remoteObjectRegistry] registerExportedObject:self interface:delegateInterface];
|
||||||
|
|
||||||
_webView = webView;
|
_webView = webView;
|
||||||
|
_webViewConfiguration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _webView;
|
return _webView;
|
||||||
@@ -90,4 +96,23 @@
|
|||||||
[_webProcessProxy setAllScriptsAllowed:allowAllScripts];
|
[_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
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user