All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.weisj.darklaf.LafManager Maven / Gradle / Ivy

There is a newer version: 3.0.2
Show newest version
/*
 * 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;

import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.*;

import com.github.weisj.darklaf.components.renderer.SimpleListCellRenderer;
import com.github.weisj.darklaf.platform.DecorationsHandler;
import com.github.weisj.darklaf.platform.ThemePreferencesHandler;
import com.github.weisj.darklaf.settings.ThemeSettings;
import com.github.weisj.darklaf.synthesised.ThemedDarklafInfo;
import com.github.weisj.darklaf.task.DefaultsAdjustmentTask;
import com.github.weisj.darklaf.task.DefaultsInitTask;
import com.github.weisj.darklaf.theme.Theme;
import com.github.weisj.darklaf.theme.event.*;
import com.github.weisj.darklaf.theme.info.PreferredThemeStyle;
import com.github.weisj.darklaf.theme.info.ThemeProvider;
import com.github.weisj.darklaf.theme.laf.ThemedLookAndFeel;
import com.github.weisj.darklaf.util.LogUtil;

/**
 * Manager for the Look and Feel.
 *
 * @author Jannis Weis
 */
public final class LafManager {

    private static final Logger LOGGER = LogUtil.getLogger(LafManager.class);
    private static ThemeProvider themeProvider;
    private static Theme theme;
    private static final List registeredThemes = new ArrayList<>();
    private static final Collection uiDefaultsTasks = new ArrayList<>();
    private static final Collection uiInitTasks = new ArrayList<>();
    private static final ThemeEventSupport eventSupport =
            new ThemeEventSupport<>();

    static {
        setLogLevel(Level.WARNING);
        ServiceLoader.load(Theme.class).forEach(LafManager::registerTheme);
    }

    /**
     * Enable logging for the Look and Feel. true means Level.INFO, false Level.SEVERE.
     *
     * @see #setLogLevel(Level)
     * @param logEnabled true if messages should be logged.
     */
    public static void enableLogging(final boolean logEnabled) {
        setLogLevel(logEnabled ? Level.INFO : Level.SEVERE);
    }

    /**
     * Sets the log level.
     *
     * @param level the new log level.
     */
    public static void setLogLevel(final Level level) {
        LogUtil.setLevel(level);
        LOGGER.fine("Setting log level to " + level);
    }

    /**
     * Get the current log level.
     *
     * @return the log level.
     */
    public static Level getLogLevel() {
        return LogUtil.getLevel();
    }

    /**
     * Returns whether custom decorations should be used. If this returns true decorations still might
     * not be used if the theme or platform don't support them.
     *
     * @return true if decorations should be used.
     */
    public static boolean isDecorationsEnabled() {
        return DecorationsHandler.getSharedInstance().isCustomDecorationSupported();
    }

    /**
     * Set globally whether decorations are enabled. By default, this is true. Decorations are used if
     * this value is set to true and the current platform and theme support custom decorations.
     *
     * @param enabled true if decorations should be used if available.
     */
    public static void setDecorationsEnabled(final boolean enabled) {
        boolean isEnabled = isDecorationsEnabled();
        DecorationsHandler.getSharedInstance().setDecorationsEnabled(enabled);
        if (isEnabled != enabled) {
            updateLaf();
        }
    }

    /**
     * Enabled whether changes in the preferred theme style should be reported to
     * {@link ThemePreferenceListener}s. On some platforms this setting may do nothing.
     *
     * 

* Warning: If preference reporting is enabled it needs to be disabled before closing the * program. Not doing so can result in memory leaks and prevent the classloader from being garbage * collected. * * @param enabled true if changes should be reported. */ public static void enabledPreferenceChangeReporting(final boolean enabled) { ThemePreferencesHandler.getSharedInstance().enablePreferenceChangeReporting(enabled); if (ThemeSettings.isInitialized()) ThemeSettings.getInstance().setSystemPreferencesEnabled(enabled); } /** * Returns whether changes to the preferred theme style should be reported to * {@link ThemePreferenceListener}s. * * @return true if enabled and currently reporting. */ public static boolean isPreferenceChangeReportingEnabled() { return ThemePreferencesHandler.getSharedInstance().isPreferenceChangeReportingEnabled(); } /** * Adds a {@link ThemePreferenceListener}. If preference change reporting is enabled the handler * will receive events if preferences have changed. * * @param listener the listener to add. * @see ThemePreferenceListener * @see #enabledPreferenceChangeReporting(boolean) * @see #isPreferenceChangeReportingEnabled() */ public static void addThemePreferenceChangeListener(final ThemePreferenceListener listener) { ThemePreferencesHandler.getSharedInstance().addThemePreferenceChangeListener(listener); } /** * Removes a {@link ThemePreferenceListener}. * * @param listener the listener to remove. * @see ThemePreferenceListener * @see #enabledPreferenceChangeReporting(boolean) * @see #isPreferenceChangeReportingEnabled() */ public static void removeThemePreferenceChangeListener(final ThemePreferenceListener listener) { ThemePreferencesHandler.getSharedInstance().removeThemePreferenceChangeListener(listener); } /** * Adds a {@link ThemeChangeListener}. The listener will receive events if the theme is changed. * * @param listener the listener to add. */ public static void addThemeChangeListener(final ThemeChangeListener listener) { eventSupport.addListener(listener); } /** * Removes a {@link ThemeChangeListener}. * * @param listener the listener to add. */ public static void removeThemeChangeListener(final ThemeChangeListener listener) { eventSupport.removeListener(listener); } /** * Gets the preferred theme style. If theme preference change reporting is enabled this may use * native os settings to determine these values. * * @return the preferred theme style. * @see #isPreferenceChangeReportingEnabled() * @see #enabledPreferenceChangeReporting(boolean) */ public static PreferredThemeStyle getPreferredThemeStyle() { return ThemePreferencesHandler.getSharedInstance().getPreferredThemeStyle(); } /** * Get the current theme provider. The theme provider is responsible the produce available themes * for a given preferred theme style. * * @return the theme provider. * @see PreferredThemeStyle */ public static ThemeProvider getThemeProvider() { if (themeProvider == null) themeProvider = createDefaultThemeProvider(); return themeProvider; } /** * Set the current theme provider. The theme provider is responsible the produce available themes * for a given preferred theme style. * * @param themeProvider the theme provider. * @see PreferredThemeStyle */ public static void setThemeProvider(final ThemeProvider themeProvider) { LafManager.themeProvider = themeProvider; } /* * Default theme provider. Defaults to IntelliJ/Darcula Light/Dark high contrast themes. */ private static ThemeProvider createDefaultThemeProvider() { return ThemeProvider.createDefault(); } /** * Get the associated theme for the given preferred style. * * @param style the preferred theme style. * @return the associated Theme or best match if there is none associated. */ public static Theme themeForPreferredStyle(final PreferredThemeStyle style) { return getThemeProvider().getTheme(style); } /** * Register all themes in array. Registered themes are returned in {@link #getRegisteredThemes()}. * * @param themes the themes to register. */ public static void registerTheme(final Theme... themes) { if (themes == null) return; for (Theme theme : themes) { registerTheme(theme); } } /** * Register a theme. Registered themes are returned in {@link #getRegisteredThemes()}. * * @param theme the theme to register. */ public static void registerTheme(final Theme theme) { registeredThemes.add(theme); } /** * Remove a register a theme. Registered themes are returned in {@link #getRegisteredThemes()}. * * @param theme the theme to register. * @return returns whether the theme was previously registered. */ public static boolean unregisterTheme(final Theme theme) { return registeredThemes.remove(theme); } /** * Get all currently registered themes. * * @return all themes currently registered as array. */ public static Theme[] getRegisteredThemes() { Theme[] themes = registeredThemes.toArray(new Theme[0]); Arrays.sort(themes); return themes; } /** * Get {@link javax.swing.UIManager.LookAndFeelInfo}s for all currently registered themes. These can * be directly used to install the specified theme. Only themes that are annotated * using @SynthesiseLaf and compiled with the corresponding annotations processor may contribute to * this list. * * @return all look and feel infos for currently registered themes as array. */ public static UIManager.LookAndFeelInfo[] getRegisteredThemeInfos() { return registeredThemes.stream().sorted().map(ThemedDarklafInfo::new).filter(ThemedDarklafInfo::exists) .toArray(UIManager.LookAndFeelInfo[]::new); } /** * Replaces a registered theme. The newTheme is only registered if the old theme is currently * registerd. * * @param oldTheme the old theme to unregister. * @param newTheme the new theme to register. */ public static void replaceTheme(final Theme oldTheme, final Theme newTheme) { if (unregisterTheme(oldTheme)) { registerTheme(newTheme); } } /** * Get a combobox model for all currently registered themes. * * @return the combo box model. */ public static ComboBoxModel getThemeComboBoxModel() { return new DefaultComboBoxModel<>(getRegisteredThemes()); } /** * Creates a {@link ListCellRenderer} for themes. * * @return the list cell render. */ public static ListCellRenderer getThemeListCellRenderer() { return SimpleListCellRenderer.create(Theme::getDisplayName); } /** * Get the current theme. This method will never return null even if the LaF isn#t currently * installed. * * @return the current theme. */ public static Theme getTheme() { if (theme == null) { setTheme(themeForPreferredStyle(null)); } return theme; } /** * Returns the currently installed theme. This differs from {@link #getTheme()} by the fact that * {@link #getTheme()} can return a different theme than the currently installed one. * * @return the currently installed theme. */ public static Theme getInstalledTheme() { LookAndFeel laf = UIManager.getLookAndFeel(); if (laf instanceof ThemedLookAndFeel) { return ((ThemedLookAndFeel) laf).getTheme(); } return getTheme(); } /** * Checks whether darklaf is currently installed. * * @return true if installed. */ public static boolean isInstalled() { return UIManager.getLookAndFeel() instanceof ThemedLookAndFeel; } /** * Set the current theme. * * @param theme The theme to use. */ public static void setTheme(final Theme theme) { Theme old = LafManager.theme; LafManager.theme = theme; if (old != theme) { eventSupport.dispatchEvent(new ThemeChangeEvent(old, theme), ThemeChangeListener::themeChanged); LOGGER.fine(() -> "Setting theme to " + theme); } if (ThemeSettings.isInitialized()) ThemeSettings.getInstance().refresh(); } /** * Install the theme reported by the {@link ThemePreferenceChangeEvent}. * * @param changeEvent the change event. */ public static void installTheme(final ThemePreferenceChangeEvent changeEvent) { setTheme(changeEvent.getPreferredThemeStyle()); install(); } /** * Install the theme suited for the given preferred theme style. * * @param preferredThemeStyle the preferred theme style. */ public static void installTheme(final PreferredThemeStyle preferredThemeStyle) { setTheme(preferredThemeStyle); install(); } /** * Sets the theme suited for the given preferred theme style. * * @param preferredThemeStyle the preferred theme style. */ public static void setTheme(final PreferredThemeStyle preferredThemeStyle) { setTheme(themeForPreferredStyle(preferredThemeStyle)); } /** * Sets the current theme and installs the LaF. If the LaF is already installed the theme is * switched. This behaves exactly like {@link #setTheme(Theme)} followed by {@link #install()} * * @param theme the theme to install. */ public static void installTheme(final Theme theme) { if (theme == getTheme() && isInstalled()) return; setTheme(theme); install(); } /** * Overload for {@link #installTheme(Theme)}. * * @param theme the theme to install. * @see #installTheme(Theme) installTheme(). */ public static void install(final Theme theme) { installTheme(theme); } /** * Install the current theme. If no theme is installed, the default is based on the current * {@link ThemeProvider}. This sets the current LaF and applies the given theme. */ public static void install() { try { getTheme(); LOGGER.fine(() -> "Installing theme " + theme); UIManager.setLookAndFeel(new DarkLaf()); updateLaf(); notifyThemeInstalled(); } catch (final UnsupportedLookAndFeelException e) { e.printStackTrace(); } } /* default */ static void notifyThemeInstalled() { eventSupport.dispatchEvent(new ThemeChangeEvent(null, getTheme()), ThemeChangeListener::themeInstalled); } /** Update the component ui classes for all current windows. */ public static void updateLaf() { for (final Window w : Window.getWindows()) { updateLafRecursively(w); } } private static void updateLafRecursively(final Window window) { for (final Window childWindow : window.getOwnedWindows()) { updateLafRecursively(childWindow); } SwingUtilities.updateComponentTreeUI(window); } /** * Register a task to modify the ui defaults. * * @param task the defaults init task. */ public static void registerDefaultsAdjustmentTask(final DefaultsAdjustmentTask task) { uiDefaultsTasks.add(task); } /** * Remove a registered task to modify the ui defaults. * * @param task the defaults init task. */ public static void removeDefaultsAdjustmentTask(final DefaultsAdjustmentTask task) { uiDefaultsTasks.remove(task); } /** * Get a view of all currently registered defaults init tasks. Modification will also mutate the * collection itself. * * @return collection of init tasks. */ public static Collection getUserDefaultsAdjustmentTasks() { return uiDefaultsTasks; } /** * Register a task to modify the laf defaults. * * @param task the defaults init task. */ public static void registerInitTask(final DefaultsInitTask task) { uiInitTasks.add(task); } /** * Remove a registered task to modify the laf defaults. * * @param task the defaults init task. */ public static void removeInitTask(final DefaultsInitTask task) { uiInitTasks.remove(task); } /** * Get a view of all currently registered ui init tasks. Modification will also mutate the * collection itself. * * @return collection of init tasks. */ public static Collection getUserInitTasks() { return uiInitTasks; } /** * Get the closest match of a registered theme for the given theme. * * @param theme the theme to match to. * @return the closes match. NonNull. */ public static Theme getClosestMatchForTheme(final Theme theme) { if (theme == null) return themeForPreferredStyle(null); for (Theme registered : getRegisteredThemes()) { if (registered.equals(theme)) return registered; } for (Theme registered : getRegisteredThemes()) { if (registered.getThemeClass().equals(theme.getThemeClass())) return registered; } return themeForPreferredStyle(null); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy