Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement auto switch of color theme based on browser API prefers-color-scheme (WIP) #86600

Closed
wants to merge 4 commits into from
Closed

Conversation

muuvmuuv
Copy link
Contributor

@muuvmuuv muuvmuuv commented Dec 9, 2019

This PR implements the functionality to auto switch the Code color theme based on the users preferred OS appearance setting. It makes use of the browser matchMedia API, which makes it compatible with the VS Code Browser version as well.

What has changed?

The colorTheme prop is as before the place where the current theme is set, but now there are three new props:

  • colorThemeAutoSwitch: this enables/disables the auto switch functionality
  • colorThemeDark: holds the theme for "dark mode"
  • colorThemeLight: holds the theme for "light mode"

VS Code will now listen on the match media event to respond to OS appearance changes. I have tested it in the browser and it works good and very smooth.

To make the code more readable and to not repeat myself, I have created some extra functions inside WorkbenchThemeService, I hope this is okay. The changes also include listening on VS Code settings changes to disable the feature if someone changes the theme manually to prevent loops or flickering. Also, I don't think someone wants this feature if he's doing it manually.

It is enabled by default, I think this is good, to force developers to take more care about their eyes in dark environments.

How to test?

  1. Download my branch
  2. yarn install
  3. yarn watch-client
  4. Open another terminal window
  5. yarn web
  6. Wait for compilation
  7. Open browser at http://localhost:8080/
  8. Open your OS appearance settings (macOS: System → General)
  9. Change the appearance

Next?

Here are the things that are still needed to finish up before a merge can be done. I also would need some help on some points. Would appreciate anyone that can comment to some of them.

  • Remove console.logs (haven't removed them for debugging ATM)
  • Remove comments
  • Make colorThemeDark and colorThemeLight selectable dropdown of theme options (Status: NEEDS HELP)
  • Update to use both options as replacement if "High contrast" is enabled
  • Add tests if possible and if needed (Status: NEEDS HELP)
  • Translation (https://github.com/Microsoft/Localization/wiki/Visual-Studio-Code-Community-Localization-Project)
  • Expose an API so extension author can make use of something like: (Status: in discussion)
    • onThemeSwitch/Change which would return the new theme or 'dark', 'light' or 'no-preference' based on the browser API
    • getPreferredScheme which would return 'dark', 'light' or 'no-preference' based on the browser API

This PR fixes #61519

This implements three new options to the workspace scope. One to enable/
disable the auto switch functionality and two others to set the dark
and light theme.
@msftclas
Copy link

msftclas commented Dec 9, 2019

CLA assistant check
All CLA requirements met.

@muuvmuuv
Copy link
Contributor Author

muuvmuuv commented Dec 9, 2019

Oh, very sorry for that much line changes... Seems like prettier does something different in my VS Code instance than in the one from the authors I guess. Would love to get some help to fix this, so it won't happen again or more strict rules in .vscode/settings.json.

Copy link
Contributor

@aeschli aeschli left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

@@ -72,6 +75,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {

private colorThemeStore: ColorThemeStore;
private currentColorTheme: ColorThemeData;
private autoSwitchColorTheme = DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE;
private autoSwitchColorThemeListener: MediaQueryList | undefined = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to follow the typical way we do this: Make this of type IDisposable | undefined.
dispose would uninstall the listener.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, autoSwitchColorTheme and autoSwitchColorThemeListener can be folded into one.

if autoSwitchColorThemeListener is defined-> autoswitch is on. if undefined: off

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 think IDisposable does not work here because matchMedia returns a MediaQueryList. I will merge it into one but will leave the MediaQueryList | undefined

@@ -114,6 +119,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
this.iconThemeStore = new FileIconThemeStore(extensionService);
this.onColorThemeChange = new Emitter<IColorTheme>({ leakWarningThreshold: 400 });

this.onPreferColorSchemeChange = this.onPreferColorSchemeChange.bind(this);
Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary?

Copy link
Contributor Author

@muuvmuuv muuvmuuv Dec 17, 2019

Choose a reason for hiding this comment

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

Needed, otherwise the function could not access WorkbenchThemeService but if you know another implementation let me know.

@@ -18,12 +18,22 @@ export const VS_HC_THEME = 'hc-black';
export const HC_THEME_ID = 'Default High Contrast';

export const COLOR_THEME_SETTING = 'workbench.colorTheme';
export const COLOR_THEME_DARK_SETTING = 'workbench.colorThemeDark';
export const COLOR_THEME_LIGHT_SETTING = 'workbench.colorThemeLight';
export const DETECT_AS_SETTING = 'workbench.colorThemeAutoSwitch';
Copy link
Contributor

Choose a reason for hiding this comment

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

Given we already have'window.autoDetectHighContrast', what about calling it:
'window.autoDetectColorScheme': true | false
and

'workbench.preferredLightColorTheme'
'workbench.preferredDarkColorTheme' 
'workbench.preferredHighConstrastColorTheme' 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thought was that even if HC is on, there can also be light and dark HC themes and give it the prefix colorTheme would make it easier to find in settings or more present to the user. Not everyone will know that this feature exists but sure preferred as prefix would make more sense and match to the browser API with prefers-color-scheme.

Would like to hear what a third or fourth person is saying about this here.


let colorThemeSetting: string;
let detectThemeAutoSwitch = this.configurationService.getValue<boolean>(DETECT_AS_SETTING);
this.autoSwitchColorTheme = detectThemeAutoSwitch;
Copy link
Contributor

@aeschli aeschli Dec 10, 2019

Choose a reason for hiding this comment

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

I think we should only switch the theme if the current theme is not of the same mode (dark/light/hc).

Do lets say I have 'Dark+' as my preferredDarkTheme, but at some point switched to use Monokai. When I enable autoSwitch. it is detected that dark is the window theme mode. But Monokai is dark, all is fine, no need to switch.
Same when VSCode is restarted.
So the 'prefferred' themes are only really used when there's a switch from the OS. The switch can be while we are open and we get the event, or while VSCode was closed, so we only see the mismatch when starting up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do lets say I have 'Dark+' as my preferredDarkTheme, but at some point switched to use Monokai. When I enable autoSwitch. it is detected that dark is the window theme mode. But Monokai is dark, all is fine, no need to switch.

You mean like this (in: e.affectsConfiguration(COLOR_THEME_SETTING))?

// if "auto switch" and (OS dark == theme dark) => update preferred dark theme
// if "auto switch" and (OS light == theme light) => update preferred light theme
// set new theme

So the 'prefferred' themes are only really used when there's a switch from the OS. The switch can be while we are open and we get the event, or while VSCode was closed, so we only see the mismatch when starting up.

Isn't this the current case?

I have updated it, please check if this is solved.

@muuvmuuv muuvmuuv deleted the auto-dark-mode branch December 19, 2019 15:58
@github-actions github-actions bot locked and limited conversation to collaborators Mar 27, 2020
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.

[themes] setting for preferred light and dark color theme based on system dark mode setting (Windows/Mac)
3 participants