Skip to content

Commit

Permalink
feat: allow manual setting of default dark/light theme
Browse files Browse the repository at this point in the history
  • Loading branch information
sebthom committed Feb 6, 2024
1 parent 34dd35a commit e87d06b
Show file tree
Hide file tree
Showing 17 changed files with 291 additions and 328 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,54 @@
*/
package org.eclipse.tm4e.ui.tests.themes;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.eclipse.tm4e.ui.internal.themes.AbstractThemeManager;
import org.eclipse.tm4e.ui.themes.ITheme;
import org.eclipse.tm4e.ui.themes.IThemeManager;
import org.eclipse.tm4e.ui.themes.Theme;
import org.eclipse.tm4e.ui.themes.ThemeIdConstants;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.osgi.service.prefs.BackingStoreException;

/**
* Test for theme manager.
*/
class ThemeManagerTest implements ThemeIdConstants {

private static final class MockThemeManager extends AbstractThemeManager {

@Override
protected void registerTheme(ITheme theme) {
super.registerTheme(theme);
}

@Override
public void save() throws BackingStoreException {
public EditSession createEditSession() {
throw new UnsupportedOperationException();
}
}

private IThemeManager manager;
private MockThemeManager manager;

@BeforeEach
public void setup() {
manager = new MockThemeManager();

// Register theme
manager.registerTheme(new Theme(SolarizedLight, "./themes/SolarizedLight.css", "SolarizedLight", false, true));
manager.registerTheme(new Theme(Light, "./themes/Light.css", "Light", false, false));
manager.registerTheme(new Theme(Dark, "./themes/Dark.css", "Dark", true, true));
manager.registerTheme(new Theme(Monokai, "./themes/Monokai.css", "Monokai", true, false));
manager.registerTheme(new Theme(SolarizedLight, "./themes/SolarizedLight.css", "SolarizedLight", false) {
@Override
public boolean isDefault() {
return true;
}
});
manager.registerTheme(new Theme(Light, "./themes/Light.css", "Light", false));
manager.registerTheme(new Theme(Dark, "./themes/Dark.css", "Dark", true) {
@Override
public boolean isDefault() {
return true;
}
});
manager.registerTheme(new Theme(Monokai, "./themes/Monokai.css", "Monokai", true));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public final class TMUIMessages extends NLS {
public static String ThemePreferencePage_title;
public static String ThemePreferencePage_description;
public static String ThemePreferencePage_column_name;
public static String ThemePreferencePage_column_type;
public static String ThemePreferencePage_column_path;
public static String ThemePreferencePage_column_pluginId;
public static String ThemePreferencePage_darkThemeButton_label;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ TaskTagsPreferencePage_editTagDialog_message=Modify an existing task tag configu
ThemePreferencePage_title=TextMate themes
ThemePreferencePage_description=&Create, edit or remove TextMate themes:
ThemePreferencePage_column_name=Name
ThemePreferencePage_column_type=Type
ThemePreferencePage_column_path=Path
ThemePreferencePage_column_pluginId=Plugin ID
ThemePreferencePage_darkThemeButton_label=Dark?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.tm4e.core.grammar.IGrammar;
import org.eclipse.tm4e.registry.ITMScope;
import org.eclipse.tm4e.ui.TMUIPlugin;
import org.eclipse.tm4e.ui.internal.utils.PreferenceUtils;
import org.eclipse.tm4e.ui.text.TMPresentationReconciler;
import org.eclipse.tm4e.ui.themes.ITheme;
import org.eclipse.tm4e.ui.themes.IThemeManager;
Expand Down Expand Up @@ -57,8 +59,8 @@ protected IContributionItem[] getContributionItems() {
final IGrammar grammar = presentationReconciler.getGrammar();
if (grammar != null) {
final IThemeManager manager = TMUIPlugin.getThemeManager();
final boolean dark = manager.isDarkEclipseTheme();
final String scopeName = grammar.getScopeName();
final boolean dark = PreferenceUtils.isDarkEclipseTheme();
final String scopeName = ITMScope.parse(grammar.getScopeName()).getName();
final ITheme selectedTheme = manager.getThemeForScope(scopeName, dark);
for (final ITheme theme : manager.getThemes()) {
final IAction action = createAction(scopeName, theme, dark);
Expand All @@ -79,7 +81,7 @@ private Action createAction(final String scopeName, final ITheme theme, final bo
return new Action(theme.getName()) {
@Override
public void run() {
final IThemeManager manager = TMUIPlugin.getThemeManager();
final IThemeManager.EditSession manager = TMUIPlugin.getThemeManager().createEditSession();
final var association = new ThemeAssociation(theme.getId(), scopeName, whenDark);
manager.registerThemeAssociation(association);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import org.eclipse.tm4e.registry.WorkingCopyGrammarRegistryManager;
import org.eclipse.tm4e.ui.TMUIPlugin;
import org.eclipse.tm4e.ui.internal.TMUIMessages;
import org.eclipse.tm4e.ui.internal.themes.WorkingCopyThemeManager;
import org.eclipse.tm4e.ui.internal.themes.ThemeManager;
import org.eclipse.tm4e.ui.internal.widgets.ContentTypesBindingWidget;
import org.eclipse.tm4e.ui.internal.widgets.GrammarInfoWidget;
Expand Down Expand Up @@ -74,7 +73,7 @@ public final class GrammarPreferencePage extends PreferencePage implements IWork
// Managers
private IGrammarRegistryManager grammarRegistryManager = new WorkingCopyGrammarRegistryManager(
TMEclipseRegistryPlugin.getGrammarRegistryManager());
private IThemeManager themeManager = new WorkingCopyThemeManager(TMUIPlugin.getThemeManager());
private final IThemeManager.EditSession themeManager = ThemeManager.getInstance().createEditSession();
private ISnippetManager snippetManager = TMUIPlugin.getSnippetManager();

// Grammar list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public final class PreferenceConstants {

public static final String E4_THEME_ID = "themeid";

public static final String DEFAULT_DARK_THEME = "org.eclipse.tm4e.ui.themes.defaultDarkTheme";
public static final String DEFAULT_LIGHT_THEME = "org.eclipse.tm4e.ui.themes.defaultLightTheme";

public static final String EDITOR_CURRENTLINE_HIGHLIGHT = "currentLineColor";

private PreferenceConstants() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,12 @@ public final class ThemePreferencePage extends PreferencePage implements IWorkbe
private TMViewer previewViewer = lazyNonNull();

private final IGrammarRegistryManager grammarRegistryManager = TMEclipseRegistryPlugin.getGrammarRegistryManager();
private final IThemeManager themeManager = TMUIPlugin.getThemeManager();
private final IThemeManager.EditSession themeManager = ThemeManager.getInstance().createEditSession();

private Button darkThemeButton = lazyNonNull();
private Button defaultThemeButton = lazyNonNull();

@Nullable
private ITheme selectedTheme;
private @Nullable ITheme selectedTheme;

public ThemePreferencePage() {
setDescription(TMUIMessages.ThemePreferencePage_description);
Expand Down Expand Up @@ -149,6 +148,7 @@ private void createThemesTableContent(final Composite parent) {
@Override
protected void createColumns() {
createAutoResizeColumn(TMUIMessages.ThemePreferencePage_column_name);
createAutoResizeColumn(TMUIMessages.ThemePreferencePage_column_type, 0);
createAutoResizeColumn(TMUIMessages.ThemePreferencePage_column_path);
createAutoResizeColumn(TMUIMessages.ThemePreferencePage_column_pluginId, 0);
}
Expand All @@ -157,8 +157,9 @@ protected void createColumns() {
protected @Nullable String getColumnText(ITheme theme, int columnIndex) {
return switch (columnIndex) {
case 0 -> theme.getName();
case 1 -> theme.getPath();
case 2 -> theme.getPluginId();
case 1 -> theme.isDark() ? "dark" : "light";
case 2 -> theme.getPath();
case 3 -> theme.getPluginId();
default -> null;
};
}
Expand All @@ -171,9 +172,10 @@ protected Object[] getElements(@Nullable Object input) {
}
};
themesTable.onSelected(selection -> {
final ITheme selectedTheme = selection.get(0);
final var selectedTheme = this.selectedTheme = selection.get(0);
darkThemeButton.setSelection(selectedTheme.isDark());
defaultThemeButton.setSelection(selectedTheme.isDefault());
darkThemeButton.setEnabled(selectedTheme.getPluginId() == null);
defaultThemeButton.setSelection(themeManager.getDefaultTheme(selectedTheme.isDark()) == selectedTheme);
themeRemoveButton.setEnabled(selectedTheme.getPluginId() == null);
preview();
});
Expand Down Expand Up @@ -211,7 +213,7 @@ private ITheme addTheme() {
}
final var file = new File(res);
final String name = file.getName().substring(0, file.getName().length() - ".css".length());
return new Theme(name, file.getAbsolutePath(), name, false, false);
return new Theme(name, file.getAbsolutePath(), name, false);
}
});

Expand Down Expand Up @@ -248,7 +250,13 @@ private void createThemeDetailContent(final Composite ancestor) {

defaultThemeButton = new Button(parent, SWT.CHECK);
defaultThemeButton.setText(TMUIMessages.ThemePreferencePage_defaultThemeButton_label);
defaultThemeButton.setEnabled(false);
defaultThemeButton.setEnabled(true);
defaultThemeButton.addListener(SWT.Selection, e -> {
final var selectedTheme = ThemePreferencePage.this.selectedTheme;
if (selectedTheme != null) {
themeManager.setDefaultTheme(selectedTheme.getId(), selectedTheme.isDark());
}
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
package org.eclipse.tm4e.ui.internal.themes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.graphics.RGB;
Expand All @@ -28,20 +28,20 @@

/**
* TextMate theme manager implementation.
*
*/
public abstract class AbstractThemeManager implements IThemeManager {

private final Map<String /* theme id */, ITheme> themes = new LinkedHashMap<>();
private final ThemeAssociationRegistry themeAssociationRegistry = new ThemeAssociationRegistry();
protected final Map<String /* theme id */, ITheme> themes = new LinkedHashMap<>();
protected final Map<@Nullable String, @Nullable IThemeAssociation> darkThemeAssociations = new HashMap<>();
protected final Map<@Nullable String, @Nullable IThemeAssociation> lightThemeAssociations = new HashMap<>();
protected @Nullable String defaultDarkThemeId;
protected @Nullable String defaultLightThemeId;

@Override
public void registerTheme(final ITheme theme) {
protected void registerTheme(final ITheme theme) {
themes.put(theme.getId(), theme);
}

@Override
public void unregisterTheme(final ITheme theme) {
protected void unregisterTheme(final ITheme theme) {
themes.remove(theme.getId());
}

Expand All @@ -57,11 +57,17 @@ public ITheme[] getThemes() {

@Override
public ITheme getDefaultTheme() {
final boolean dark = isDarkEclipseTheme();
return getDefaultTheme(dark);
return getDefaultTheme(PreferenceUtils.isDarkEclipseTheme());
}

ITheme getDefaultTheme(final boolean dark) {
@Override
public ITheme getDefaultTheme(final boolean dark) {
final var defaultThemeId = dark ? defaultDarkThemeId : defaultLightThemeId;
final var defaultTheme = defaultThemeId == null ? null : themes.get(defaultThemeId);
if (defaultTheme != null) {
return defaultTheme;
}

for (final ITheme theme : themes.values()) {
if (theme.isDark() == dark && theme.isDefault()) {
return theme;
Expand All @@ -70,31 +76,30 @@ ITheme getDefaultTheme(final boolean dark) {
throw new IllegalStateException("Should never be reached");
}

@Override
public ITheme[] getThemes(final boolean dark) {
return themes.values().stream().filter(theme -> theme.isDark() == dark).toArray(ITheme[]::new);
}

@Override
public boolean isDarkEclipseTheme() {
return isDarkEclipseTheme(PreferenceUtils.getE4PreferenceCSSThemeId());
protected void setDefaultTheme(final String themeId, final boolean dark) {
if (dark)
defaultDarkThemeId = themeId;
else
defaultLightThemeId = themeId;
}

@Override
public boolean isDarkEclipseTheme(@Nullable final String eclipseThemeId) {
return eclipseThemeId != null && eclipseThemeId.toLowerCase().contains("dark");
public ITheme[] getThemes(final boolean dark) {
return themes.values().stream().filter(theme -> theme.isDark() == dark).toArray(ITheme[]::new);
}

@Override
public ITheme getThemeForScope(final String scopeName) {
return getThemeForScope(scopeName, isDarkEclipseTheme());
return getThemeForScope(scopeName, PreferenceUtils.isDarkEclipseTheme());
}

@Override
public ITheme getThemeForScope(String scopeName, final boolean dark) {
scopeName = ITMScope.parse(scopeName).getName();

final IThemeAssociation association = themeAssociationRegistry.getThemeAssociationFor(scopeName, dark);
final IThemeAssociation association = dark
? darkThemeAssociations.get(scopeName)
: lightThemeAssociations.get(scopeName);
if (association != null) {
final String themeId = association.getThemeId();
final var theme = getThemeById(themeId);
Expand All @@ -117,32 +122,38 @@ public IThemeAssociation[] getThemeAssociationsForScope(String scopeName) {
scopeName = ITMScope.parse(scopeName).getName();

final var associations = new ArrayList<IThemeAssociation>();
IThemeAssociation light = themeAssociationRegistry.getThemeAssociationFor(scopeName, false);
IThemeAssociation light = lightThemeAssociations.get(scopeName);
if (light == null) {
light = new ThemeAssociation(getDefaultTheme(false).getId(), scopeName, false);
}
associations.add(light);
IThemeAssociation dark = themeAssociationRegistry.getThemeAssociationFor(scopeName, true);
IThemeAssociation dark = darkThemeAssociations.get(scopeName);
if (dark == null) {
dark = new ThemeAssociation(getDefaultTheme(true).getId(), scopeName, true);
}
associations.add(dark);
return associations.toArray(IThemeAssociation[]::new);
}

@Override
public void registerThemeAssociation(final IThemeAssociation association) {
themeAssociationRegistry.register(association);
protected void registerThemeAssociation(final IThemeAssociation association) {
if (association.isWhenDark()) {
darkThemeAssociations.put(association.getScopeName(), association);
} else {
lightThemeAssociations.put(association.getScopeName(), association);
}
}

@Override
public void unregisterThemeAssociation(final IThemeAssociation association) {
themeAssociationRegistry.unregister(association);
protected void unregisterThemeAssociation(final IThemeAssociation association) {
if (association.isWhenDark()) {
darkThemeAssociations.remove(association.getScopeName(), association);
} else {
lightThemeAssociations.remove(association.getScopeName(), association);
}
}

@Override
public IThemeAssociation[] getAllThemeAssociations() {
final List<IThemeAssociation> associations = themeAssociationRegistry.getThemeAssociations();
return associations.toArray(IThemeAssociation[]::new);
return Stream.concat(darkThemeAssociations.values().stream(), lightThemeAssociations.values().stream())
.toArray(IThemeAssociation[]::new);
}
}
Loading

0 comments on commit e87d06b

Please sign in to comment.