diff --git a/core/src/main/java/com/github/weisj/darklaf/DarkLaf.java b/core/src/main/java/com/github/weisj/darklaf/DarkLaf.java index 21a408ea..e455af9e 100644 --- a/core/src/main/java/com/github/weisj/darklaf/DarkLaf.java +++ b/core/src/main/java/com/github/weisj/darklaf/DarkLaf.java @@ -23,32 +23,16 @@ */ package com.github.weisj.darklaf; -import com.github.weisj.darklaf.components.border.DarkBorders; import com.github.weisj.darklaf.platform.Decorations; -import com.github.weisj.darklaf.theme.FontSizeRule; +import com.github.weisj.darklaf.task.*; import com.github.weisj.darklaf.theme.Theme; import com.github.weisj.darklaf.ui.DarkPopupFactory; import com.github.weisj.darklaf.ui.popupmenu.DarkPopupMenuUI; -import com.github.weisj.darklaf.util.DarkUIUtil; import com.github.weisj.darklaf.util.SystemInfo; import javax.swing.*; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicLookAndFeel; import javax.swing.plaf.metal.MetalLookAndFeel; -import javax.swing.text.DefaultEditorKit; -import javax.swing.text.html.HTMLEditorKit; -import javax.swing.text.html.StyleSheet; -import java.awt.*; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.font.TextAttribute; -import java.text.AttributedCharacterIterator; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; @@ -59,7 +43,18 @@ public class DarkLaf extends BasicLookAndFeel { public static final String SYSTEM_PROPERTY_PREFIX = "darklaf."; private static final Logger LOGGER = Logger.getLogger(DarkLaf.class.getName()); - private static final String NAME = "Darklaf"; + /* + * All tasks for initializing the ui defaults in order of execution. + */ + private static final DefaultsInitTask[] INIT_TASKS = new DefaultsInitTask[]{ + new ThemeDefaultsInitTask(), + new InputDefaultsInitTask(), + new IdeaDefaultsInitTask(), + new FontDefaultsInitTask(), + new UtilityDefaultsInitTask(), + new SystemDefaultsInitTask(), + new PlatformDefaultsInitTask() + }; private final BasicLookAndFeel base; /** @@ -100,85 +95,15 @@ public class DarkLaf extends BasicLookAndFeel { } } - @Override - public UIDefaults getDefaults() { - final UIDefaults metalDefaults = new MetalLookAndFeel().getDefaults(); - final UIDefaults defaults = base.getDefaults(); - final Theme currentTheme = LafManager.getTheme(); - try { - initInputMapDefaults(defaults); - loadThemeDefaults(currentTheme, defaults); - setupUtils(defaults); - initIdeaDefaults(defaults); - - patchComboBox(metalDefaults, defaults); - if (SystemInfo.isMac) { - patchMacOSFonts(defaults); - } - applyFontRule(currentTheme, defaults); - setupDecorations(); - - String key = DarkPopupMenuUI.KEY_DEFAULT_LIGHTWEIGHT_POPUPS; - if (SystemInfo.isWindows10 && Decorations.isCustomDecorationSupported()) { - JPopupMenu.setDefaultLightWeightPopupEnabled(defaults.getBoolean(key + ".windows")); - } else { - JPopupMenu.setDefaultLightWeightPopupEnabled(defaults.getBoolean(key)); - } - - // Update border colors. - DarkBorders.update(defaults); - return defaults; - } catch (final Exception e) { - LOGGER.log(Level.SEVERE, e.toString(), e.getStackTrace()); - } - return defaults; - } - - private void applyFontRule(final Theme currentTheme, final UIDefaults defaults) { - FontSizeRule rule = currentTheme.getFontSizeRule(); - if (rule == null || rule == FontSizeRule.DEFAULT) return; - for (Map.Entry entry : defaults.entrySet()) { - if (entry != null && entry.getValue() instanceof Font) { - entry.setValue(fontWithRule((Font) entry.getValue(), currentTheme, rule, defaults)); - } - } - } - - private Font fontWithRule(final Font font, final Theme currentTheme, - final FontSizeRule rule, final UIDefaults defaults) { - Font withRule = currentTheme.getFontMapper(rule).map(font, defaults); - if (font instanceof UIResource - && !(withRule instanceof UIResource)) { - withRule = new FontUIResource(withRule); - } - return withRule; - } - - private void setupUtils(final UIDefaults defaults) { - DarkUIUtil.setDropOpacity(defaults.getInt("dropOpacity") / 100f); - DarkUIUtil.setGlowOpacity(defaults.getInt("glowOpacity") / 100f); - DarkUIUtil.setShadowOpacity(defaults.getInt("shadowOpacity") / 100f); - } - - private void patchMacOSFonts(final UIDefaults defaults) { - for (Map.Entry entry : defaults.entrySet()) { - if (entry.getValue() instanceof Font) { - Font font = (Font) entry.getValue(); - entry.setValue(macOSFontFromFont(font)); - } - } - } - - private Font macOSFontFromFont(final Font font) { - Map attributes = Collections.singletonMap(TextAttribute.KERNING, - TextAttribute.KERNING_ON); - String fontName = SystemInfo.isMacOSCatalina ? ".AppleSystemUIFont" : ".SF NS Text"; - Font macFont = new Font(fontName, font.getStyle(), font.getSize()).deriveFont(attributes); - if (font instanceof UIResource) { - macFont = new FontUIResource(macFont); - } - return macFont == null ? font : macFont; + public void initialize() { + /* + * On macOS the default PopupFactory is overwritten with a custom one, which always uses heavyweight popups. + * This is disadvantageous for the behaviour of custom tooltips. + */ + base.initialize(); + PopupFactory.setSharedInstance(new DarkPopupFactory()); + setupDecorations(); } private void setupDecorations() { @@ -187,157 +112,28 @@ public class DarkLaf extends BasicLookAndFeel { JDialog.setDefaultLookAndFeelDecorated(true); } - protected void adjustPlatformSpecifics(final Properties uiProps) { - boolean useScreenMenuBar = Boolean.getBoolean("apple.laf.useScreenMenuBar"); - // If user wants to use Apple menu bar, then we need to keep the default - // component for MenuBarUI and MenuUI - if (SystemInfo.isMac && useScreenMenuBar) { - uiProps.remove("MenuBarUI"); + @Override + public void uninitialize() { + base.uninitialize(); + DarkPopupMenuUI.MouseGrabber mouseGrabber = DarkPopupMenuUI.getMouseGrabber(); + if (mouseGrabber != null) { + mouseGrabber.uninstall(); } } - @Override - public String getName() { - return NAME; - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - private static void initInputMapDefaults(final UIDefaults defaults) { - // Make ENTER work in JTrees - final InputMap treeInputMap = (InputMap) defaults.get("Tree.focusInputMap"); - if (treeInputMap != null) { - // it's really possible. For example, GTK+ doesn't have such map - treeInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "toggle"); - } - // Cut/Copy/Paste in JTextAreas - final InputMap textAreaInputMap = (InputMap) defaults.get("TextArea.focusInputMap"); - if (textAreaInputMap != null) { - // It really can be null, for example when LAF isn't properly initialized - // (Alloy license problem) - installCutCopyPasteShortcuts(textAreaInputMap, false); - } - // Cut/Copy/Paste in JTextFields - final InputMap textFieldInputMap = (InputMap) defaults.get("TextField.focusInputMap"); - if (textFieldInputMap != null) { - // It really can be null, for example when LAF isn't properly initialized - // (Alloy license problem) - installCutCopyPasteShortcuts(textFieldInputMap, false); - } - // Cut/Copy/Paste in JPasswordField - final InputMap passwordFieldInputMap = (InputMap) defaults.get("PasswordField.focusInputMap"); - if (passwordFieldInputMap != null) { - // It really can be null, for example when LAF isn't properly initialized - // (Alloy license problem) - installCutCopyPasteShortcuts(passwordFieldInputMap, false); - } - // Cut/Copy/Paste in JTables - final InputMap tableInputMap = (InputMap) defaults.get("Table.ancestorInputMap"); - if (tableInputMap != null) { - // It really can be null, for example when LAF isn't properly initialized - // (Alloy license problem) - installCutCopyPasteShortcuts(tableInputMap, true); - } - final InputMap buttonInputMap = (InputMap) defaults.get("Button.focusInputMap"); - if (buttonInputMap != null && !SystemInfo.isMac) { - buttonInputMap.put(KeyStroke.getKeyStroke("ENTER"), "pressed"); - buttonInputMap.put(KeyStroke.getKeyStroke("released ENTER"), "released"); + public UIDefaults getDefaults() { + final UIDefaults defaults = base.getDefaults(); + final Theme currentTheme = LafManager.getTheme(); + for (DefaultsInitTask task : INIT_TASKS) { + task.run(currentTheme, defaults); } + return defaults; } - private void loadThemeDefaults(final Theme currentTheme, final UIDefaults defaults) { - Properties uiProps = new Properties(); - currentTheme.loadDefaults(uiProps, defaults); - //Load overwrites the user has set. - PropertyLoader.putProperties(LafManager.getUserProperties(), uiProps, defaults); - - currentTheme.loadGlobals(uiProps, defaults); - installGlobals(uiProps, defaults); - loadFontProperties(uiProps, defaults); - currentTheme.loadUIProperties(uiProps, defaults); - currentTheme.loadIconProperties(uiProps, defaults); - Decorations.loadDecorationProperties(uiProps, defaults); - currentTheme.loadPlatformProperties(uiProps, defaults); - adjustPlatformSpecifics(uiProps); - loadSystemOverwrites(uiProps, defaults); - defaults.putAll(uiProps); - - StyleSheet styleSheet = currentTheme.loadStyleSheet(); - new HTMLEditorKit().setStyleSheet(styleSheet); - } - - private void loadFontProperties(final Properties uiProps, final UIDefaults defaults) { - Properties fontSizeProps = PropertyLoader.loadProperties(DarkLaf.class, "font_sizes", "properties/"); - PropertyLoader.putProperties(fontSizeProps, uiProps, defaults); - Properties fontProps = PropertyLoader.loadProperties(DarkLaf.class, "font", "properties/"); - PropertyLoader.putProperties(fontProps, uiProps, defaults); - } - - private void loadSystemOverwrites(final Properties uiProps, final UIDefaults defaults) { - Properties overwrites = PropertyLoader.loadProperties(DarkLaf.class, "overwrites", "properties/"); - overwrites.values().removeIf(v -> System.getProperty(SYSTEM_PROPERTY_PREFIX + v.toString()) == null); - overwrites.entrySet().forEach( - e -> e.setValue(System.getProperty(SYSTEM_PROPERTY_PREFIX + e.getValue().toString()))); - PropertyLoader.putProperties(overwrites, uiProps, defaults); - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - private void initIdeaDefaults(final UIDefaults defaults) { - defaults.put("Table.ancestorInputMap", new UIDefaults.LazyInputMap( - new Object[]{ - "ctrl C", "copy", - "meta C", "copy", - "ctrl V", "paste", - "meta V", "paste", - "ctrl X", "cut", - "meta X", "cut", - "COPY", "copy", - "PASTE", "paste", - "CUT", "cut", - "control INSERT", "copy", - "shift INSERT", "paste", - "shift DELETE", "cut", - "RIGHT", "selectNextColumn", - "KP_RIGHT", "selectNextColumn", - "LEFT", "selectPreviousColumn", - "KP_LEFT", "selectPreviousColumn", - "DOWN", "selectNextRow", - "KP_DOWN", "selectNextRow", - "UP", "selectPreviousRow", - "KP_UP", "selectPreviousRow", - "shift RIGHT", "selectNextColumnExtendSelection", - "shift KP_RIGHT", "selectNextColumnExtendSelection", - "shift LEFT", "selectPreviousColumnExtendSelection", - "shift KP_LEFT", "selectPreviousColumnExtendSelection", - "shift DOWN", "selectNextRowExtendSelection", - "shift KP_DOWN", "selectNextRowExtendSelection", - "shift UP", "selectPreviousRowExtendSelection", - "shift KP_UP", "selectPreviousRowExtendSelection", - "PAGE_UP", "scrollUpChangeSelection", - "PAGE_DOWN", "scrollDownChangeSelection", - "HOME", "selectFirstColumn", - "END", "selectLastColumn", - "shift PAGE_UP", "scrollUpExtendSelection", - "shift PAGE_DOWN", "scrollDownExtendSelection", - "shift HOME", "selectFirstColumnExtendSelection", - "shift END", "selectLastColumnExtendSelection", - "ctrl PAGE_UP", "scrollLeftChangeSelection", - "ctrl PAGE_DOWN", "scrollRightChangeSelection", - "ctrl HOME", "selectFirstRow", - "ctrl END", "selectLastRow", - "ctrl shift PAGE_UP", "scrollRightExtendSelection", - "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection", - "ctrl shift HOME", "selectFirstRowExtendSelection", - "ctrl shift END", "selectLastRowExtendSelection", - "TAB", "selectNextColumnCell", - "shift TAB", "selectPreviousColumnCell", - "ENTER", "selectNextRowCell", - "shift ENTER", "selectPreviousRowCell", - "ctrl A", "selectAll", - "meta A", "selectAll", - "ESCAPE", "cancel", - "F2", "startEditing" - })); + @Override + public String getName() { + return "Darklaf"; } @@ -346,74 +142,12 @@ public class DarkLaf extends BasicLookAndFeel { return getName(); } - private static void patchComboBox(final UIDefaults metalDefaults, final UIDefaults defaults) { - defaults.remove("ComboBox.ancestorInputMap"); - defaults.remove("ComboBox.actionMap"); - defaults.put("ComboBox.ancestorInputMap", metalDefaults.get("ComboBox.ancestorInputMap")); - defaults.put("ComboBox.actionMap", metalDefaults.get("ComboBox.actionMap")); - } - - private static void installCutCopyPasteShortcuts(final InputMap inputMap, - final boolean useSimpleActionKeys) { - final String copyActionKey = useSimpleActionKeys ? "copy" : DefaultEditorKit.copyAction; - final String pasteActionKey = useSimpleActionKeys ? "paste" : DefaultEditorKit.pasteAction; - final String cutActionKey = useSimpleActionKeys ? "cut" : DefaultEditorKit.cutAction; - final int mask = SystemInfo.isMac ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK; - // Ctrl+Ins, Shift+Ins, Shift+Del - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, InputEvent.CTRL_DOWN_MASK), copyActionKey); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, InputEvent.SHIFT_DOWN_MASK), pasteActionKey); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK), cutActionKey); - // Ctrl+C, Ctrl+V, Ctrl+X - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, mask), copyActionKey); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, mask), pasteActionKey); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, mask), DefaultEditorKit.cutAction); - } - - private void installGlobals(final Properties uiProps, final UIDefaults defaults) { - final HashMap globalSettings = new HashMap<>(); - final String prefix = "global."; - for (final Object key : uiProps.keySet()) { - if (key instanceof String && ((String) key).startsWith(prefix)) { - globalSettings.put(((String) key).substring(prefix.length()), uiProps.get(key)); - } - } - - for (final Object key : defaults.keySet()) { - if (key instanceof String && ((String) key).contains(".")) { - final String s = (String) key; - final String globalKey = s.substring(s.lastIndexOf('.') + 1); - if (globalSettings.containsKey(globalKey)) { - defaults.put(key, globalSettings.get(globalKey)); - } - } - } - } - @Override public String getDescription() { return "Dark Look and feel based on Darcula-LAF"; } - @Override - public void initialize() { - /* - * On macOS the default PopupFactory is overwritten with a custom one, which always uses heavyweight popups. - * This is disadvantageous for the behaviour of custom tooltips. - */ - base.initialize(); - PopupFactory.setSharedInstance(new DarkPopupFactory()); - } - - @Override - public void uninitialize() { - base.uninitialize(); - DarkPopupMenuUI.MouseGrabber mouseGrabber = DarkPopupMenuUI.getMouseGrabber(); - if (mouseGrabber != null) { - mouseGrabber.uninstall(); - } - } - @Override public boolean isNativeLookAndFeel() { return true; @@ -428,5 +162,4 @@ public class DarkLaf extends BasicLookAndFeel { public boolean getSupportsWindowDecorations() { return Decorations.isCustomDecorationSupported(); } - } diff --git a/core/src/main/java/com/github/weisj/darklaf/LafManager.java b/core/src/main/java/com/github/weisj/darklaf/LafManager.java index 23b3f168..19d84819 100644 --- a/core/src/main/java/com/github/weisj/darklaf/LafManager.java +++ b/core/src/main/java/com/github/weisj/darklaf/LafManager.java @@ -25,6 +25,7 @@ package com.github.weisj.darklaf; import com.github.weisj.darklaf.icons.AwareIconStyle; import com.github.weisj.darklaf.icons.IconLoader; +import com.github.weisj.darklaf.task.DefaultsInitTask; import com.github.weisj.darklaf.theme.*; import javax.swing.*; @@ -32,7 +33,8 @@ import java.awt.*; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; -import java.util.Properties; +import java.util.Collection; +import java.util.HashSet; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; @@ -48,7 +50,7 @@ public final class LafManager { private static Theme theme; private static boolean logEnabled = false; private static boolean decorationsOverwrite = true; - private static final Properties properties = new Properties(); + private static final Collection uiDefaultsTasks = new HashSet<>(); static { enableLogging(true); @@ -240,60 +242,30 @@ public final class LafManager { } /** - * Set a custom property. - *

- * Note: These properties are loaded after {@link Theme#loadDefaults(Properties, UIDefaults)} and should only be - * used to overwrite the values specified in `[theme]_defaults.properties`. + * Register a task to modify the ui defaults. * - * @param key the key. - * @param value the value. + * @param task the defaults init task. */ - public static void setProperty(final String key, final String value) { - properties.setProperty(key, value); + public static void registerDefaultsInitTask(final DefaultsInitTask task) { + uiDefaultsTasks.add(task); } /** - * Remove a custom property. - *

- * Note: These properties are loaded after {@link Theme#loadDefaults(Properties, UIDefaults)} and should only be - * used to overwrite the values specified in `[theme]_defaults.properties`. + * Remove a registered task to modify the ui defaults. * - * @param key the key. + * @param task the defaults init task. */ - public static void removeProperty(final String key) { - properties.remove(key); + public static void removeDefaultsInitTask(final DefaultsInitTask task) { + uiDefaultsTasks.remove(task); } /** - * Remove all custom properties. - *

- * Note: These properties are loaded after {@link Theme#loadDefaults(Properties, UIDefaults)} and should only be - * used to overwrite the values specified in `[theme]_defaults.properties`. - */ - public static void clearProperties() { - properties.clear(); - } - - /** - * Get the custom properties. - *

- * Note: These properties are loaded after {@link Theme#loadDefaults(Properties, UIDefaults)} and should only be - * used to overwrite the values specified in `[theme]_defaults.properties`. - */ - public static Properties getUserProperties() { - return properties; - } - - /** - * Remove a custom property. - *

- * Note: These properties are loaded after {@link Theme#loadDefaults(Properties, UIDefaults)} and should only be - * used to overwrite the values specified in `[theme]_defaults.properties`. + * Get a view of all currently registered init tasks. Modification will also mutate the collection itself. * - * @param key the key. - * @return the value associated with `key`. + * @return collection of init tasks. */ - public String getProperty(final String key) { - return properties.getProperty(key); + public static Collection getUserInitTasks() { + return uiDefaultsTasks; } + } diff --git a/core/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java b/core/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java index e37471d4..6490a5d8 100644 --- a/core/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java +++ b/core/src/main/java/com/github/weisj/darklaf/components/border/DarkBorders.java @@ -63,16 +63,21 @@ public final class DarkBorders { return createBorder(top, left, bottom, right, lineWidgetBorderMap, "borderSecondary"); } - public static void update(final UIDefaults defaults) { - Color borderColor = defaults.getColor("border"); + public static void update(final Map defaults) { + Color borderColor = getColor(defaults, "border"); for (WeakReference border : lineBorderMap.values()) { WeakLineBorder b = border.get(); if (b != null) b.setColor(borderColor); } - Color borderSecondaryColor = defaults.getColor("borderSecondary"); + Color borderSecondaryColor = getColor(defaults, "borderSecondary"); for (WeakReference border : lineWidgetBorderMap.values()) { WeakLineBorder b = border.get(); if (b != null) b.setColor(borderSecondaryColor); } } + + private static Color getColor(final Map defaults, final String key) { + Object color = defaults.get(key); + return color instanceof Color ? (Color) color : null; + } } diff --git a/core/src/main/java/com/github/weisj/darklaf/platform/Decorations.java b/core/src/main/java/com/github/weisj/darklaf/platform/Decorations.java index 4d0b1df3..53eb34af 100644 --- a/core/src/main/java/com/github/weisj/darklaf/platform/Decorations.java +++ b/core/src/main/java/com/github/weisj/darklaf/platform/Decorations.java @@ -34,6 +34,7 @@ import com.github.weisj.darklaf.util.SystemInfo; import javax.swing.*; import java.awt.*; +import java.util.Map; import java.util.Properties; public final class Decorations { @@ -83,7 +84,7 @@ public final class Decorations { decorationsProvider.initialize(); } - public static void loadDecorationProperties(final Properties uiProps, final UIDefaults defaults) { + public static void loadDecorationProperties(final Properties uiProps, final Map defaults) { decorationsProvider.loadDecorationProperties(uiProps, defaults); } } diff --git a/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java b/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java index 9d511966..bd3599b6 100644 --- a/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java +++ b/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java @@ -28,6 +28,7 @@ import com.github.weisj.darklaf.decorations.DecorationsProvider; import javax.swing.*; import java.awt.*; +import java.util.Map; import java.util.Properties; public class DefaultDecorationsProvider implements DecorationsProvider { @@ -59,6 +60,6 @@ public class DefaultDecorationsProvider implements DecorationsProvider { } @Override - public void loadDecorationProperties(final Properties properties, final UIDefaults currentDefaults) { + public void loadDecorationProperties(final Properties properties, final Map currentDefaults) { } } diff --git a/core/src/main/java/com/github/weisj/darklaf/task/DefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/DefaultsInitTask.java new file mode 100644 index 00000000..ba6b4b30 --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/DefaultsInitTask.java @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.theme.Theme; + +import java.util.Map; + +public interface DefaultsInitTask { + + void run(final Theme currentTheme, final Map defaults); +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/FontDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/FontDefaultsInitTask.java new file mode 100644 index 00000000..88bd59ba --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/FontDefaultsInitTask.java @@ -0,0 +1,114 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.DarkLaf; +import com.github.weisj.darklaf.PropertyLoader; +import com.github.weisj.darklaf.theme.FontMapper; +import com.github.weisj.darklaf.theme.FontSizeRule; +import com.github.weisj.darklaf.theme.Theme; +import com.github.weisj.darklaf.util.SystemInfo; + +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.awt.font.TextAttribute; +import java.text.AttributedCharacterIterator; +import java.util.Collections; +import java.util.Map; +import java.util.Properties; + +public class FontDefaultsInitTask implements DefaultsInitTask { + + private static final String FONT_PROPERTY_PATH = "properties/"; + private static final String FONT_SIZE_DEFAULTS_NAME = "font_sizes"; + private static final String FONT_DEFAULTS_NAME = "font_sizes"; + + private static final String MAC_OS_CATALINA_FONT_NAME = ".AppleSystemUIFont"; + private static final String MAC_OS_FONT_NAME = ".SF NS Text"; + + @Override + public void run(final Theme currentTheme, final Map defaults) { + loadFontProperties(defaults); + if (SystemInfo.isMac) { + patchMacOSFonts(defaults); + } + applyFontRule(currentTheme, defaults); + } + + private void loadFontProperties(final Map defaults) { + Properties fontSizeProps = PropertyLoader.loadProperties(DarkLaf.class, + FONT_SIZE_DEFAULTS_NAME, + FONT_PROPERTY_PATH); + PropertyLoader.putProperties(fontSizeProps, defaults); + Properties fontProps = PropertyLoader.loadProperties(DarkLaf.class, + FONT_DEFAULTS_NAME, + FONT_PROPERTY_PATH); + PropertyLoader.putProperties(fontProps, defaults); + } + + private void patchMacOSFonts(final Map defaults) { + for (Map.Entry entry : defaults.entrySet()) { + if (entry.getValue() instanceof Font) { + Font font = (Font) entry.getValue(); + entry.setValue(macOSFontFromFont(font)); + } + } + } + + private Font macOSFontFromFont(final Font font) { + Map attributes + = Collections.singletonMap(TextAttribute.KERNING, TextAttribute.KERNING_ON); + String fontName = SystemInfo.isMacOSCatalina ? MAC_OS_CATALINA_FONT_NAME : MAC_OS_FONT_NAME; + Font macFont = new Font(fontName, font.getStyle(), font.getSize()).deriveFont(attributes); + if (font instanceof UIResource) { + macFont = new FontUIResource(macFont); + } + return macFont == null ? font : macFont; + } + + private void applyFontRule(final Theme currentTheme, final Map defaults) { + FontSizeRule rule = currentTheme.getFontSizeRule(); + if (rule == null || rule == FontSizeRule.DEFAULT) return; + for (Map.Entry entry : defaults.entrySet()) { + if (entry != null && entry.getValue() instanceof Font) { + entry.setValue(fontWithRule((Font) entry.getValue(), rule, defaults)); + } + } + } + + private Font fontWithRule(final Font font, final FontSizeRule rule, final Map defaults) { + Font withRule = getFontMapper(rule).map(font, defaults); + if (font instanceof UIResource + && !(withRule instanceof UIResource)) { + withRule = new FontUIResource(withRule); + } + return withRule; + } + + private FontMapper getFontMapper(final FontSizeRule rule) { + if (rule == null) return (font, defaults) -> font; + return rule.getFontMapper(); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/IdeaDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/IdeaDefaultsInitTask.java new file mode 100644 index 00000000..3f258eef --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/IdeaDefaultsInitTask.java @@ -0,0 +1,95 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.theme.Theme; + +import javax.swing.*; +import java.util.Map; + +public class IdeaDefaultsInitTask implements DefaultsInitTask { + @Override + public void run(final Theme currentTheme, final Map defaults) { + initIdeaDefaults(defaults); + } + + @SuppressWarnings({"HardCodedStringLiteral"}) + private void initIdeaDefaults(final Map defaults) { + defaults.put("Table.ancestorInputMap", new UIDefaults.LazyInputMap( + new Object[]{ + "ctrl C", "copy", + "meta C", "copy", + "ctrl V", "paste", + "meta V", "paste", + "ctrl X", "cut", + "meta X", "cut", + "COPY", "copy", + "PASTE", "paste", + "CUT", "cut", + "control INSERT", "copy", + "shift INSERT", "paste", + "shift DELETE", "cut", + "RIGHT", "selectNextColumn", + "KP_RIGHT", "selectNextColumn", + "LEFT", "selectPreviousColumn", + "KP_LEFT", "selectPreviousColumn", + "DOWN", "selectNextRow", + "KP_DOWN", "selectNextRow", + "UP", "selectPreviousRow", + "KP_UP", "selectPreviousRow", + "shift RIGHT", "selectNextColumnExtendSelection", + "shift KP_RIGHT", "selectNextColumnExtendSelection", + "shift LEFT", "selectPreviousColumnExtendSelection", + "shift KP_LEFT", "selectPreviousColumnExtendSelection", + "shift DOWN", "selectNextRowExtendSelection", + "shift KP_DOWN", "selectNextRowExtendSelection", + "shift UP", "selectPreviousRowExtendSelection", + "shift KP_UP", "selectPreviousRowExtendSelection", + "PAGE_UP", "scrollUpChangeSelection", + "PAGE_DOWN", "scrollDownChangeSelection", + "HOME", "selectFirstColumn", + "END", "selectLastColumn", + "shift PAGE_UP", "scrollUpExtendSelection", + "shift PAGE_DOWN", "scrollDownExtendSelection", + "shift HOME", "selectFirstColumnExtendSelection", + "shift END", "selectLastColumnExtendSelection", + "ctrl PAGE_UP", "scrollLeftChangeSelection", + "ctrl PAGE_DOWN", "scrollRightChangeSelection", + "ctrl HOME", "selectFirstRow", + "ctrl END", "selectLastRow", + "ctrl shift PAGE_UP", "scrollRightExtendSelection", + "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection", + "ctrl shift HOME", "selectFirstRowExtendSelection", + "ctrl shift END", "selectLastRowExtendSelection", + "TAB", "selectNextColumnCell", + "shift TAB", "selectPreviousColumnCell", + "ENTER", "selectNextRowCell", + "shift ENTER", "selectPreviousRowCell", + "ctrl A", "selectAll", + "meta A", "selectAll", + "ESCAPE", "cancel", + "F2", "startEditing" + })); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java new file mode 100644 index 00000000..74f750ba --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/InputDefaultsInitTask.java @@ -0,0 +1,108 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.theme.Theme; +import com.github.weisj.darklaf.util.SystemInfo; + +import javax.swing.*; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.text.DefaultEditorKit; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.Map; + +public class InputDefaultsInitTask implements DefaultsInitTask { + + @Override + public void run(final Theme currentTheme, final Map defaults) { + initInputMapDefaults(defaults); + patchComboBox(new MetalLookAndFeel().getDefaults(), defaults); + } + + private void initInputMapDefaults(final Map defaults) { + // Make ENTER work in JTrees + final InputMap treeInputMap = (InputMap) defaults.get("Tree.focusInputMap"); + if (treeInputMap != null) { + // it's really possible. For example, GTK+ doesn't have such map + treeInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "toggle"); + } + // Cut/Copy/Paste in JTextAreas + final InputMap textAreaInputMap = (InputMap) defaults.get("TextArea.focusInputMap"); + if (textAreaInputMap != null) { + // It really can be null, for example when LAF isn't properly initialized + // (Alloy license problem) + installCutCopyPasteShortcuts(textAreaInputMap, false); + } + // Cut/Copy/Paste in JTextFields + final InputMap textFieldInputMap = (InputMap) defaults.get("TextField.focusInputMap"); + if (textFieldInputMap != null) { + // It really can be null, for example when LAF isn't properly initialized + // (Alloy license problem) + installCutCopyPasteShortcuts(textFieldInputMap, false); + } + // Cut/Copy/Paste in JPasswordField + final InputMap passwordFieldInputMap = (InputMap) defaults.get("PasswordField.focusInputMap"); + if (passwordFieldInputMap != null) { + // It really can be null, for example when LAF isn't properly initialized + // (Alloy license problem) + installCutCopyPasteShortcuts(passwordFieldInputMap, false); + } + // Cut/Copy/Paste in JTables + final InputMap tableInputMap = (InputMap) defaults.get("Table.ancestorInputMap"); + if (tableInputMap != null) { + // It really can be null, for example when LAF isn't properly initialized + // (Alloy license problem) + installCutCopyPasteShortcuts(tableInputMap, true); + } + final InputMap buttonInputMap = (InputMap) defaults.get("Button.focusInputMap"); + if (buttonInputMap != null && !SystemInfo.isMac) { + buttonInputMap.put(KeyStroke.getKeyStroke("ENTER"), "pressed"); + buttonInputMap.put(KeyStroke.getKeyStroke("released ENTER"), "released"); + } + } + + private void installCutCopyPasteShortcuts(final InputMap inputMap, + final boolean useSimpleActionKeys) { + final String copyActionKey = useSimpleActionKeys ? "copy" : DefaultEditorKit.copyAction; + final String pasteActionKey = useSimpleActionKeys ? "paste" : DefaultEditorKit.pasteAction; + final String cutActionKey = useSimpleActionKeys ? "cut" : DefaultEditorKit.cutAction; + final int mask = SystemInfo.isMac ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK; + // Ctrl+Ins, Shift+Ins, Shift+Del + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, InputEvent.CTRL_DOWN_MASK), copyActionKey); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, InputEvent.SHIFT_DOWN_MASK), pasteActionKey); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK), cutActionKey); + // Ctrl+C, Ctrl+V, Ctrl+X + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, mask), copyActionKey); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, mask), pasteActionKey); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, mask), DefaultEditorKit.cutAction); + } + + private static void patchComboBox(final UIDefaults metalDefaults, final Map defaults) { + defaults.remove("ComboBox.ancestorInputMap"); + defaults.remove("ComboBox.actionMap"); + defaults.put("ComboBox.ancestorInputMap", metalDefaults.get("ComboBox.ancestorInputMap")); + defaults.put("ComboBox.actionMap", metalDefaults.get("ComboBox.actionMap")); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/PlatformDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/PlatformDefaultsInitTask.java new file mode 100644 index 00000000..f47f76eb --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/PlatformDefaultsInitTask.java @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.platform.Decorations; +import com.github.weisj.darklaf.theme.Theme; +import com.github.weisj.darklaf.ui.popupmenu.DarkPopupMenuUI; +import com.github.weisj.darklaf.util.SystemInfo; + +import javax.swing.*; +import java.util.Map; + +public class PlatformDefaultsInitTask implements DefaultsInitTask { + @Override + public void run(final Theme currentTheme, final Map defaults) { + String key = DarkPopupMenuUI.KEY_DEFAULT_LIGHTWEIGHT_POPUPS; + if (SystemInfo.isWindows10 && Decorations.isCustomDecorationSupported()) { + JPopupMenu.setDefaultLightWeightPopupEnabled(Boolean.TRUE.equals(defaults.get(key + ".windows"))); + } else { + JPopupMenu.setDefaultLightWeightPopupEnabled(Boolean.TRUE.equals(defaults.get(key))); + } + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/SystemDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/SystemDefaultsInitTask.java new file mode 100644 index 00000000..6d5f3528 --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/SystemDefaultsInitTask.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.DarkLaf; +import com.github.weisj.darklaf.PropertyLoader; +import com.github.weisj.darklaf.theme.Theme; + +import java.util.Map; +import java.util.Properties; + +public class SystemDefaultsInitTask implements DefaultsInitTask { + + private static final String OVERWRITES_PATH = "properties/"; + private static final String OVERWRITES_NAME = "overwrites"; + + @Override + public void run(final Theme currentTheme, final Map defaults) { + loadSystemOverwrites(defaults); + } + + private void loadSystemOverwrites(final Map defaults) { + Properties overwrites = PropertyLoader.loadProperties(DarkLaf.class, OVERWRITES_NAME, OVERWRITES_PATH); + overwrites.values().removeIf(v -> System.getProperty(DarkLaf.SYSTEM_PROPERTY_PREFIX + v.toString()) == null); + overwrites.entrySet().forEach( + e -> e.setValue(System.getProperty(DarkLaf.SYSTEM_PROPERTY_PREFIX + e.getValue().toString()))); + PropertyLoader.putProperties(overwrites, defaults); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/ThemeDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/ThemeDefaultsInitTask.java new file mode 100644 index 00000000..e1e6272c --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/ThemeDefaultsInitTask.java @@ -0,0 +1,99 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.platform.Decorations; +import com.github.weisj.darklaf.theme.Theme; +import com.github.weisj.darklaf.util.SystemInfo; + +import javax.swing.*; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class ThemeDefaultsInitTask implements DefaultsInitTask { + + private static final String GLOBAL_PREFIX = "global."; + private static final String MAC_OS_MENU_BAR_KEY = "apple.laf.useScreenMenuBar"; + private final DefaultsInitTask userPreferenceInitTask = new UserPreferenceInitTask(); + + @Override + public void run(final Theme currentTheme, final Map defaults) { + if (!(defaults instanceof UIDefaults)) return; + loadThemeDefaults(currentTheme, (UIDefaults) defaults); + } + + private void loadThemeDefaults(final Theme currentTheme, final UIDefaults defaults) { + Properties uiProps = new Properties(); + currentTheme.loadDefaults(uiProps, defaults); + + /* + * User preferences need to be applied here so changes are applied to all + * components that use the property. + */ + userPreferenceInitTask.run(currentTheme, uiProps); + + currentTheme.loadGlobals(uiProps, defaults); + installGlobals(uiProps, defaults); + currentTheme.loadUIProperties(uiProps, defaults); + currentTheme.loadIconProperties(uiProps, defaults); + Decorations.loadDecorationProperties(uiProps, defaults); + currentTheme.loadPlatformProperties(uiProps, defaults); + adjustPlatformSpecifics(uiProps); + defaults.putAll(uiProps); + + StyleSheet styleSheet = currentTheme.loadStyleSheet(); + new HTMLEditorKit().setStyleSheet(styleSheet); + } + + private void installGlobals(final Properties uiProps, final Map defaults) { + final HashMap globalSettings = new HashMap<>(); + for (final Object key : uiProps.keySet()) { + if (key instanceof String && ((String) key).startsWith(GLOBAL_PREFIX)) { + globalSettings.put(((String) key).substring(GLOBAL_PREFIX.length()), uiProps.get(key)); + } + } + + for (final Object key : defaults.keySet()) { + if (key instanceof String && ((String) key).contains(".")) { + final String s = (String) key; + final String globalKey = s.substring(s.lastIndexOf('.') + 1); + if (globalSettings.containsKey(globalKey)) { + defaults.put(key, globalSettings.get(globalKey)); + } + } + } + } + + protected void adjustPlatformSpecifics(final Properties uiProps) { + boolean useScreenMenuBar = Boolean.getBoolean(MAC_OS_MENU_BAR_KEY); + // If user wants to use Apple menu bar, then we need to keep the default + // component for MenuBarUI and MenuUI + if (SystemInfo.isMac && useScreenMenuBar) { + uiProps.remove("MenuBarUI"); + } + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/UserPreferenceInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/UserPreferenceInitTask.java new file mode 100644 index 00000000..3017a5fa --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/UserPreferenceInitTask.java @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.LafManager; +import com.github.weisj.darklaf.theme.Theme; + +import java.util.Map; + +public class UserPreferenceInitTask implements DefaultsInitTask { + + @Override + public void run(final Theme currentTheme, final Map defaults) { + for (DefaultsInitTask task : LafManager.getUserInitTasks()) { + if (task != null) task.run(currentTheme, defaults); + } + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/task/UtilityDefaultsInitTask.java b/core/src/main/java/com/github/weisj/darklaf/task/UtilityDefaultsInitTask.java new file mode 100644 index 00000000..fddf03c3 --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/task/UtilityDefaultsInitTask.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.weisj.darklaf.task; + +import com.github.weisj.darklaf.components.border.DarkBorders; +import com.github.weisj.darklaf.theme.Theme; +import com.github.weisj.darklaf.util.DarkUIUtil; + +import java.util.Map; + +public class UtilityDefaultsInitTask implements DefaultsInitTask { + @Override + public void run(final Theme currentTheme, final Map defaults) { + setupUtils(defaults); + } + + private void setupUtils(final Map defaults) { + DarkUIUtil.setDropOpacity(getOpacity(defaults, "dropOpacity")); + DarkUIUtil.setGlowOpacity(getOpacity(defaults, "glowOpacity")); + DarkUIUtil.setShadowOpacity(getOpacity(defaults, "shadowOpacity")); + DarkBorders.update(defaults); + } + + private float getOpacity(final Map defaults, final String key) { + Object obj = defaults.get(key); + int val = (obj instanceof Integer) ? (int) obj : 100; + return val / 100f; + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/theme/FontMapper.java b/core/src/main/java/com/github/weisj/darklaf/theme/FontMapper.java index 4c9be729..e4b7daa7 100644 --- a/core/src/main/java/com/github/weisj/darklaf/theme/FontMapper.java +++ b/core/src/main/java/com/github/weisj/darklaf/theme/FontMapper.java @@ -23,10 +23,10 @@ */ package com.github.weisj.darklaf.theme; -import javax.swing.*; import java.awt.*; +import java.util.Map; public interface FontMapper { - Font map(final Font font, final UIDefaults defaults); + Font map(final Font font, final Map defaults); } diff --git a/core/src/main/java/com/github/weisj/darklaf/theme/PropertyFontMapper.java b/core/src/main/java/com/github/weisj/darklaf/theme/PropertyFontMapper.java index fd78c4f9..dee11a07 100644 --- a/core/src/main/java/com/github/weisj/darklaf/theme/PropertyFontMapper.java +++ b/core/src/main/java/com/github/weisj/darklaf/theme/PropertyFontMapper.java @@ -25,8 +25,8 @@ package com.github.weisj.darklaf.theme; import com.github.weisj.darklaf.LafManager; -import javax.swing.*; import java.awt.*; +import java.util.Map; import java.util.logging.Logger; public class PropertyFontMapper implements FontMapper { @@ -42,7 +42,7 @@ public class PropertyFontMapper implements FontMapper { } @Override - public Font map(final Font font, final UIDefaults defaults) { + public Font map(final Font font, final Map defaults) { adjustment = getSize(defaults); // No need to create a new font. if (adjustment == 0) return font; @@ -56,7 +56,7 @@ public class PropertyFontMapper implements FontMapper { return font.deriveFont(font.getSize2D() + adjustment); } - private int getSize(final UIDefaults defaults) { + private int getSize(final Map defaults) { // Use cached value if already queried. if (lastTheme == LafManager.getTheme()) return adjustment; lastTheme = LafManager.getTheme(); diff --git a/core/src/main/java/com/github/weisj/darklaf/theme/Theme.java b/core/src/main/java/com/github/weisj/darklaf/theme/Theme.java index bf4cdb20..4cf5ced8 100644 --- a/core/src/main/java/com/github/weisj/darklaf/theme/Theme.java +++ b/core/src/main/java/com/github/weisj/darklaf/theme/Theme.java @@ -368,16 +368,4 @@ public abstract class Theme { this.fontSizeRule = fontSizeRule; } - /** - * Get the font mapper for the specified size rule. By default, this delegated to the values specified by the - * `[rule].size` properties. - * - * @param rule the font size rule. - * @return the associated font mapper. - */ - public FontMapper getFontMapper(final FontSizeRule rule) { - if (rule == null) return (font, defaults) -> font; - return rule.getFontMapper(); - } - } diff --git a/decorations-base/src/main/java/com/github/weisj/darklaf/decorations/DecorationsProvider.java b/decorations-base/src/main/java/com/github/weisj/darklaf/decorations/DecorationsProvider.java index d169e294..2497ea10 100644 --- a/decorations-base/src/main/java/com/github/weisj/darklaf/decorations/DecorationsProvider.java +++ b/decorations-base/src/main/java/com/github/weisj/darklaf/decorations/DecorationsProvider.java @@ -25,6 +25,7 @@ package com.github.weisj.darklaf.decorations; import javax.swing.*; import java.awt.*; +import java.util.Map; import java.util.Properties; public interface DecorationsProvider { @@ -58,7 +59,7 @@ public interface DecorationsProvider { * @param properties the properties to load the values into. * @param currentDefaults the current ui defaults. */ - void loadDecorationProperties(Properties properties, UIDefaults currentDefaults); + void loadDecorationProperties(Properties properties, Map currentDefaults); /** * Initialize the window of a popup menu. diff --git a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java index 7ccb0934..6efaa5e7 100644 --- a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java +++ b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java @@ -31,6 +31,7 @@ import com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane; import javax.swing.*; import java.awt.*; +import java.util.Map; import java.util.Properties; public class MacOSDecorationsProvider implements DecorationsProvider { @@ -51,7 +52,7 @@ public class MacOSDecorationsProvider implements DecorationsProvider { } @Override - public void loadDecorationProperties(final Properties properties, final UIDefaults currentDefaults) { + public void loadDecorationProperties(final Properties properties, final Map currentDefaults) { IconLoader iconLoader = IconLoader.get(MacOSDecorationsProvider.class); PropertyLoader.putProperties(PropertyLoader.loadProperties(MacOSDecorationsProvider.class, "macos_decorations", ""), diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/PropertyLoader.java b/property-loader/src/main/java/com/github/weisj/darklaf/PropertyLoader.java index 05cea998..fc7e7780 100644 --- a/property-loader/src/main/java/com/github/weisj/darklaf/PropertyLoader.java +++ b/property-loader/src/main/java/com/github/weisj/darklaf/PropertyLoader.java @@ -98,14 +98,29 @@ public final class PropertyLoader { } public static void putProperties(final Properties properties, final Properties accumulator, - final UIDefaults currentDefaults) { + final Map currentDefaults) { putProperties(properties, accumulator, currentDefaults, ICON_LOADER); } public static void putProperties(final Properties properties, final Properties accumulator, - final UIDefaults currentDefaults, final IconLoader iconLoader) { - for (final String key : properties.stringPropertyNames()) { - final String value = properties.getProperty(key); + final Map currentDefaults, final IconLoader iconLoader) { + putProperties(properties, properties.stringPropertyNames(), accumulator, currentDefaults, iconLoader); + } + + public static void putProperties(final Properties properties, final Map defaults) { + putProperties(properties, defaults, ICON_LOADER); + } + + public static void putProperties(final Properties properties, final Map defaults, + final IconLoader iconLoader) { + putProperties(properties, properties.stringPropertyNames(), defaults, defaults, iconLoader); + } + + public static void putProperties(final Map properties, final Set keys, + final Map accumulator, + final Map currentDefaults, final IconLoader iconLoader) { + for (final String key : keys) { + final String value = properties.get(key).toString(); Object parsed = parseValue(key, value, accumulator, currentDefaults, iconLoader); if (parsed != null) { accumulator.put(parseKey(key), parsed); @@ -115,11 +130,6 @@ public final class PropertyLoader { } } - private static Object parseValue(final String key, final String value, final Properties accumulator, - final UIDefaults currentDefaults, final IconLoader iconLoader) { - return parseValue(key, value, false, accumulator, currentDefaults, iconLoader); - } - private static String parseKey(final String key) { if (addReferenceInfo) return key; return key.startsWith(String.valueOf(REFERENCE_PREFIX)) ? key.substring(1) : key; @@ -127,13 +137,13 @@ public final class PropertyLoader { private static Object parseValue(final String propertyKey, final String value, - final boolean ignoreRequest, final Map accumulator, - final UIDefaults currentDefaults, final IconLoader iconLoader) { + final Map accumulator, + final Map currentDefaults, final IconLoader iconLoader) { if (PropertyValue.NULL.equals(value)) { return null; } String key = propertyKey; - boolean skipObjects = ignoreRequest; + boolean skipObjects = false; if (key.startsWith(String.valueOf(REFERENCE_PREFIX))) { key = parseKey(key); skipObjects = true; @@ -209,7 +219,7 @@ public final class PropertyLoader { @SuppressWarnings("MagicConstant") private static Object parseFont(final String key, final String value, final Map accumulator, - final UIDefaults currentDefaults) { + final Map currentDefaults) { String val = value; Font base = null; int size = -1; @@ -233,7 +243,7 @@ public final class PropertyLoader { } if (base == null) base = parseExplicitFont(value); if (base == null && accumulator.get(key) instanceof Font) base = (Font) accumulator.get(key); - if (base == null) base = currentDefaults.getFont(key); + if (base == null && currentDefaults.get(key) instanceof Font) base = (Font) currentDefaults.get(key); if (base == null) base = new Font("Dialog", Font.PLAIN, 12); if (size > 0) base = base.deriveFont((float) size); if (style >= 0) base = base.deriveFont(style); @@ -278,14 +288,14 @@ public final class PropertyLoader { private static Pair parseFrom(final String val, final Map accumulator, - final UIDefaults currentDefaults) { + final Map currentDefaults) { String key = val.substring(FONT_FROM.length() + 1); int index = key.indexOf(ARG_END); String rest = key.substring(index + 1); key = key.substring(0, index); Font font = null; if (accumulator.get(key) instanceof Font) font = (Font) accumulator.get(key); - if (font == null) font = currentDefaults.getFont(key); + if (font == null && currentDefaults.get(key) instanceof Font) font = (Font) currentDefaults.get(key); return new Pair<>(font, rest); } diff --git a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java index b6381ebf..8324e1fd 100644 --- a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java +++ b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java @@ -34,6 +34,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.Map; import java.util.Properties; public class WindowsDecorationsProvider implements DecorationsProvider { @@ -82,7 +83,7 @@ public class WindowsDecorationsProvider implements DecorationsProvider { } @Override - public void loadDecorationProperties(final Properties properties, final UIDefaults currentDefaults) { + public void loadDecorationProperties(final Properties properties, final Map currentDefaults) { IconLoader iconLoader = IconLoader.get(WindowsDecorationsProvider.class); PropertyLoader.putProperties(PropertyLoader.loadProperties(WindowsDecorationsProvider.class, "windows_decorations", ""),