Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #1493: Themes / Dark Mode Support #1324

Merged
merged 87 commits into from
Sep 6, 2019
Merged

Fix #1493: Themes / Dark Mode Support #1324

merged 87 commits into from
Sep 6, 2019

Conversation

jhreis
Copy link
Contributor

@jhreis jhreis commented Aug 2, 2019

Summary of Changes

This pull request fixes issue #1493

This ticket impacts a good bit, so tried to include a good bit of code comments, as well as PR comments too.

Make sure to squash this!! Lots of commits 😄

Submitter Checklist:

  • Unit Tests are updated to cover new or changed functionality
  • User-facing strings use NSLocalizableString()

Test Plan:

Screenshots:

Reviewer Checklist:

  • Issues include necessary QA labels:
    • QA/(Yes|No)
    • release-notes/(include|exclude)
    • bug / enhancement
  • Necessary security reviews have taken place.
  • Adequate unit test coverage exists to prevent regressions.
  • Adequate test plan exists for QA to validate (if applicable).
  • Issue is assigned to a milestone (should happen at merge time).

@jhreis jhreis force-pushed the themes branch 2 times, most recently from 2f80463 to 1aa1f3e Compare August 10, 2019 01:06
@jhreis jhreis mentioned this pull request Sep 6, 2019
6 tasks
@@ -243,13 +243,6 @@ class TabManager: NSObject {
}
if let tab = selectedTab {
TabEvent.post(.didGainFocus, for: tab)

switch tab.type {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this to AppearanceExtensions.swift

let shareButton = ToolbarButton()
let addTabButton = ToolbarButton()
let menuButton = ToolbarButton()
let tabsButton = TabsButton(top: false)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit annoying, but needed some way to differentiate when TabsButton or ToolbarButton required certain tint elements.

This is largely caused by these buttons handling their own internal theming, which is not great.

Filed a follow up ticket for this: #1494

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I thought it was initial work to support fully custom toolbars, and its placement

case .add: return #imageLiteral(resourceName: "menu-add-bookmark")
case .share: return #imageLiteral(resourceName: "nav-share")
case .downloads: return #imageLiteral(resourceName: "menu-downloads")
case .bookmarks: return #imageLiteral(resourceName: "menu_bookmarks").template
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.template is required to properly style these buttons via tintColor or similar.

}

private var theme: Theme {
Theme.of(tabManager.selectedTab)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit annoying, but necessary since theme can dynamically change while settings is up, either by theme change or by toggling Private Browsing Only.

@@ -167,6 +170,24 @@ class SettingsViewController: TableViewController {
general.rows.append(row)
}

let themeSubtitle = Theme.DefaultTheme(rawValue: Preferences.General.themeNormalMode.value)?.displayString
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the theme option to settings. Currently only manipulates the normal theme option.

onValueChange: {
Preferences.Privacy.privateBrowsingOnly.value = $0

// Need to flush the table, hacky, but works consistenly and well
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When switching into PrivateBrowsingOnly, the settings UI needs to be updated, this is where that happens.

@@ -1,57 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

fileprivate enum ThemeCodingKeys: String, CodingKey {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For JSON mappings.

@@ -0,0 +1,336 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big class. Takes and decodes the JSON files, and utilizes these for theming. Currently a lot of the heavy lifting is done via: DefaultTheme, this was mostly done for time, but there are some complexities involved with fully dynamic themes (e.g. localizations), that still need to be figured out.

So should be a thorough enough solution for any "hardcoded" / hard-included themes we ship in the binary.

let theme = Theme.of(tab)
applyTheme(theme)
let newTheme = Theme.of(tab)
if previous == nil || newTheme != Theme.of(previous) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched from using tab.type to using theme change detection. Also, will "force" update the theme if the previous tab didn't exist.

@jhreis jhreis changed the title Themes Fix #1493: Themes / Dark Mode Support Sep 6, 2019
@jhreis jhreis requested a review from iccub September 6, 2019 05:44
@jhreis jhreis marked this pull request as ready for review September 6, 2019 05:44
Copy link
Contributor

@iccub iccub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good overall, few small changes and questions.
I also have two big-picture concerns:

My first feeling was that there is not enough UX constants in the json file to support all views.
This leads to:

  1. Let’s say I create a new view, how do I know what value to use for my views?
    Look at this picture for example

Zrzut ekranu 2019-09-6 o 11 45 59

One view uses some magic value header, the other one home while both views have tint color taken from header.
These values don't make much sense, we would need to use trail and error to figure out correct values, or our design team has to tell us to use header, home instead of hard color values.

If we start supporting custom themes this can also lead to unexpected colors. If the header color looks good in view X, it might not look good in view Y, and we won't know that unless we test for all themes including the custom ones

EDIT: This point will apply later after we start adding custom themes with the new tab experience etc.
2. I feel like we should have separate values for new tab page UI, and for general components. I supposed some people/publishers might want to have a cool new tab experience with pink colors and other bling bling, but for regular browsing you don't necessarily want to have that pink tint everywhere.
This would also help with my concern from point number 1

These 2 points are not blockers by any means, it's just what came up to my mind while reviewing the code


// MARK: - Themes

public static let ThemesDisplayBrightness = NSLocalizedString("ThemesDisplayBrightness", tableName: "BraveShared", bundle: Bundle.braveShared, value: "Display & Brightness", comment: "Setting to choose the user interface theme for normal browsing mode, contains choices like 'light' or 'dark' themes")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember Sriram talking about changing this setting title, have we decided on it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't heard anything, we're going with this for now, can adjust in future 🤷‍♂

UINavigationBar.appearance().tintColor = colors.accent
UINavigationBar.appearance().appearanceBarTintColor = colors.header

// UISwitch.appearance().tintColor = colors.accent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

@@ -47,12 +47,12 @@ class OnboardingNavigationController: UINavigationController {
case shieldsInfo

/// Returns new ViewController associated with the screen type
func viewController(with profile: Profile) -> OnboardingViewController {
func viewController(with profile: Profile, theme: Theme) -> OnboardingViewController {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we passing Theme here because selectedTab is not available yet?
Could we use theme.of nil so it infers the theme based on preferences/privacy mode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, mostly just no reason for on-boarding to get a tab, just giving it the minimal of what it needs. At some point it may not even need that.

We may be refactoring on-boarding to be light/dark vs being created from a theme file.

// In order to properly apply background and align this with the rest of the UI (keyboard / header)
// this needs to be called. UI could be handled internally to view systems,
// but then keyboard may misalign with Brave selected theme override
Theme.of(nil).applyAppearanceProperties()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we could have another Theme method for getting theme by preference/privacy mode, Theme.of(nil) isn't readable, you need to dig into the method to see what is gonna happen

something like Theme.default or Theme.byPrivacyMode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main thing is ideally this is quite rare. I ma primary hesitant to use a .default or .mode since it should be so rarely used I don't want to make it super convenient. Otherwise, it may just be used naively.

@@ -89,7 +89,9 @@ class ReaderModeBarView: UIView {
extension ReaderModeBarView: Themeable {

func applyTheme(_ theme: Theme) {
backgroundColor = UIColor.Browser.Background.colorFor(theme)
buttonTintColor = UIColor.Browser.Tint.colorFor(theme)
styleChildren(theme: theme)
Copy link
Contributor

@iccub iccub Sep 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to myself: there are no themable children in this view. I think it's still a good practice to always call styleChildren

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment to the protocol with this info, although not technically enforceable. Similar to calling super on overridden methods I suppose.

tabDataSource.tabs = tabManager.tabsForCurrentMode
applyTheme(privateMode ? .private : .regular)
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convert to /// comment lines. Emojis can stay 🤣

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to using // as /// is generally used for method / class documentation. but got rid of the /* */ thing 😄

let shareButton = ToolbarButton()
let addTabButton = ToolbarButton()
let menuButton = ToolbarButton()
let tabsButton = TabsButton(top: false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I thought it was initial work to support fully custom toolbars, and its placement

@@ -144,6 +144,7 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {

override func loadView() {
view = View()
shieldsView.set(theme: Theme.of(tab))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can't use applyTheme/styleChildren here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, fixed.

case `private` = "C5CB0D9A-5467-432C-AB35-1A78C55CFB41"

// TODO: Theme: Remove with `rawValue`
var id: String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be replaced with rawValue as the comment says

@jhreis
Copy link
Contributor Author

jhreis commented Sep 6, 2019

  1. Yes, this is a pretty serious issue. I think we would generally be able to define themes that work for our current decisions, but it will break if people use "unusual" theming choices (e.g. exact same header / footer / home colors). You also mention the user expectation for themes, and this is a real thing too. Setting the header color adjusted the settings table view.

It was a hard balance to identify some theme file that would work relatively well with fewer items.

One approach that we may take in the future is defining not specific colors but genre's of colors, like primary, secondary, etc...

Something that would require some more serious thought and be planned out in conjunction with Design. The current system basically requires us to audit themes pretty closely

  1. Agreed, potentially related to point # 1.

Copy link
Contributor

@iccub iccub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. It must have been a lot of effort to make the app look good for all 3 themes, good job on that :)
I ran some tests on my devices and haven't noticed any bad UI, we should be good to go

@jhreis jhreis merged commit 45787a9 into development Sep 6, 2019
@jhreis jhreis deleted the themes branch September 6, 2019 16:57
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants