package com.megatim.manuel.core.theme;
|
|
import javafx.scene.Parent;
|
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.UncheckedIOException;
|
import java.net.URL;
|
import java.nio.charset.StandardCharsets;
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* Charte graphique du manuel : couleurs du viewer JavaFX, identité (titre, logo),
|
* feuilles de style additionnelles et variables des pages HTML générées.
|
*
|
* <p>Immuable ; se construit via {@link #builder()} (pré-rempli avec les valeurs par
|
* défaut, charte Sirius-FT) ou {@link #defaultTheme()}.</p>
|
*
|
* <p>Mécanique côté viewer : la CSS embarquée déclare des <em>looked-up colors</em>
|
* ({@code -mgt-accent}, {@code -mgt-button}, ...) sur la classe {@code .help-viewer}
|
* et les référence partout ; {@link #applyTo(Parent)} injecte les valeurs du thème
|
* via {@code setStyle} puis attache les feuilles de style. Une application hôte peut
|
* donc changer la charte sans fournir de CSS, ou surcharger finement via
|
* {@code extraViewerStylesheets} (les {@code styleClass} du viewer sont un contrat
|
* stable : {@code help-viewer}, {@code button-primary}, {@code help-left-panel},
|
* {@code help-project-title}, {@code help-status}, ...).</p>
|
*
|
* <p>Mécanique côté pages HTML : {@link #renderHtmlStyles()} rend le template
|
* {@code styles.css} (embarqué ou fourni via {@code htmlStylesTemplate}) en
|
* substituant les variables {@code ${html.*}} (surchargeables une à une via
|
* {@code htmlVar(...)}).</p>
|
*/
|
public final class ManuelTheme {
|
|
/** Feuille de style par défaut du viewer (looked-up colors), embarquée dans la lib. */
|
public static final String DEFAULT_VIEWER_CSS =
|
"/com/megatim/manuel/core/viewer/help-viewer.css";
|
/** Template par défaut du styles.css des pages HTML générées. */
|
public static final String DEFAULT_HTML_TEMPLATE =
|
"/com/megatim/manuel/core/theme/html-styles-template.css";
|
|
// --- Couleurs viewer (défauts = charte Sirius) ---
|
private final String accentColor;
|
private final String buttonColor;
|
private final String buttonBorderColor;
|
private final String panelBorderColor;
|
private final String selectionColor;
|
private final String hoverColor;
|
private final String textColor;
|
private final String backgroundColor;
|
|
// --- Identité ---
|
private final String appTitle;
|
private final URL logo;
|
|
// --- CSS viewer ---
|
private final String viewerStylesheet;
|
private final List<String> extraViewerStylesheets;
|
|
// --- CSS HTML ---
|
private final String htmlStylesTemplate;
|
private final Map<String, String> htmlVars;
|
|
private ManuelTheme(Builder b) {
|
this.accentColor = b.accentColor;
|
this.buttonColor = b.buttonColor;
|
this.buttonBorderColor = b.buttonBorderColor;
|
this.panelBorderColor = b.panelBorderColor;
|
this.selectionColor = b.selectionColor;
|
this.hoverColor = b.hoverColor;
|
this.textColor = b.textColor;
|
this.backgroundColor = b.backgroundColor;
|
this.appTitle = b.appTitle;
|
this.logo = b.logo;
|
this.viewerStylesheet = b.viewerStylesheet;
|
this.extraViewerStylesheets = Collections.unmodifiableList(new ArrayList<>(b.extraViewerStylesheets));
|
this.htmlStylesTemplate = b.htmlStylesTemplate;
|
this.htmlVars = Collections.unmodifiableMap(new LinkedHashMap<>(b.htmlVars));
|
}
|
|
/** Thème par défaut : charte Sirius-FT. */
|
public static ManuelTheme defaultTheme() {
|
return builder().build();
|
}
|
|
/** Builder pré-rempli avec les valeurs par défaut (charte Sirius-FT). */
|
public static Builder builder() {
|
return new Builder();
|
}
|
|
/**
|
* Applique le thème à la racine du viewer : injection des looked-up colors puis
|
* attachement de la CSS par défaut (ou de remplacement) et des surcharges.
|
*/
|
public void applyTo(Parent root) {
|
root.setStyle("-mgt-accent: " + accentColor + ";"
|
+ " -mgt-button: " + buttonColor + ";"
|
+ " -mgt-button-border: " + buttonBorderColor + ";"
|
+ " -mgt-panel-border: " + panelBorderColor + ";"
|
+ " -mgt-selection: " + selectionColor + ";"
|
+ " -mgt-hover: " + hoverColor + ";"
|
+ " -mgt-text: " + textColor + ";"
|
+ " -mgt-bg: " + backgroundColor + ";");
|
if (viewerStylesheet != null) {
|
root.getStylesheets().add(viewerStylesheet);
|
} else {
|
URL css = ManuelTheme.class.getResource(DEFAULT_VIEWER_CSS);
|
if (css != null) {
|
root.getStylesheets().add(css.toExternalForm());
|
}
|
}
|
root.getStylesheets().addAll(extraViewerStylesheets);
|
}
|
|
/** Rend le contenu du styles.css des pages HTML (template + variables du thème). */
|
public String renderHtmlStyles() {
|
String template = htmlStylesTemplate != null ? htmlStylesTemplate : loadDefaultHtmlTemplate();
|
Map<String, String> vars = defaultHtmlVars();
|
vars.putAll(htmlVars);
|
String out = template;
|
for (Map.Entry<String, String> e : vars.entrySet()) {
|
out = out.replace("${" + e.getKey() + "}", e.getValue());
|
}
|
return out;
|
}
|
|
/** Variables HTML par défaut (charte Sirius) ; base de la substitution du template. */
|
public static Map<String, String> defaultHtmlVars() {
|
Map<String, String> v = new LinkedHashMap<>();
|
v.put("html.font", "'Segoe UI',Tahoma,Arial,sans-serif");
|
v.put("html.text", "#1a1a1a");
|
v.put("html.bg", "#ffffff");
|
// Charte Sirius-FT : anthracite/violet, aucun bleu
|
v.put("html.h1", "#3d4d5c");
|
v.put("html.h2", "#5d4a75");
|
v.put("html.h3", "#756784");
|
v.put("html.link", "#756784");
|
v.put("html.caption", "#444444");
|
v.put("html.muted", "#555555");
|
v.put("html.rule", "#d0d0d0");
|
v.put("html.navbar.bg", "#f1edf5");
|
v.put("html.navbar.border", "#d5cbdf");
|
v.put("html.navbar.disabled", "#a8b0ba");
|
v.put("html.table.border", "#b0b8c4");
|
v.put("html.img.border", "#c0c0c0");
|
return v;
|
}
|
|
private static String loadDefaultHtmlTemplate() {
|
try (InputStream in = ManuelTheme.class.getResourceAsStream(DEFAULT_HTML_TEMPLATE)) {
|
if (in == null) {
|
throw new IOException("Resource introuvable : " + DEFAULT_HTML_TEMPLATE);
|
}
|
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
|
} catch (IOException e) {
|
throw new UncheckedIOException(e);
|
}
|
}
|
|
public String getAccentColor() { return accentColor; }
|
public String getButtonColor() { return buttonColor; }
|
public String getButtonBorderColor() { return buttonBorderColor; }
|
public String getPanelBorderColor() { return panelBorderColor; }
|
public String getSelectionColor() { return selectionColor; }
|
public String getHoverColor() { return hoverColor; }
|
public String getTextColor() { return textColor; }
|
public String getBackgroundColor() { return backgroundColor; }
|
public String getAppTitle() { return appTitle; }
|
public URL getLogo() { return logo; }
|
public String getViewerStylesheet() { return viewerStylesheet; }
|
public List<String> getExtraViewerStylesheets() { return extraViewerStylesheets; }
|
public String getHtmlStylesTemplate() { return htmlStylesTemplate; }
|
public Map<String, String> getHtmlVars() { return htmlVars; }
|
|
/** Builder mutable ; toutes les valeurs sont pré-remplies charte Sirius. */
|
public static final class Builder {
|
private String accentColor = "#e9e4ef";
|
private String buttonColor = "#f0f0f0";
|
private String buttonBorderColor = "#bac0c0";
|
private String panelBorderColor = "#d1d1d1";
|
private String selectionColor = "#D6ECDB";
|
private String hoverColor = "#f0f6f2";
|
private String textColor = "#323232";
|
private String backgroundColor = "#ffffff";
|
private String appTitle;
|
private URL logo;
|
private String viewerStylesheet;
|
private final List<String> extraViewerStylesheets = new ArrayList<>();
|
private String htmlStylesTemplate;
|
private final Map<String, String> htmlVars = new LinkedHashMap<>();
|
|
public Builder accentColor(String v) { this.accentColor = v; return this; }
|
public Builder buttonColor(String v) { this.buttonColor = v; return this; }
|
public Builder buttonBorderColor(String v) { this.buttonBorderColor = v; return this; }
|
public Builder panelBorderColor(String v) { this.panelBorderColor = v; return this; }
|
public Builder selectionColor(String v) { this.selectionColor = v; return this; }
|
public Builder hoverColor(String v) { this.hoverColor = v; return this; }
|
public Builder textColor(String v) { this.textColor = v; return this; }
|
public Builder backgroundColor(String v) { this.backgroundColor = v; return this; }
|
public Builder appTitle(String v) { this.appTitle = v; return this; }
|
public Builder logo(URL v) { this.logo = v; return this; }
|
/** Remplace entièrement la CSS embarquée du viewer (URI de feuille de style). */
|
public Builder viewerStylesheet(String uri) { this.viewerStylesheet = uri; return this; }
|
/** Ajoute une CSS de surcharge, chargée après la CSS par défaut. */
|
public Builder extraViewerStylesheet(String uri) { this.extraViewerStylesheets.add(uri); return this; }
|
/** Remplace entièrement le template styles.css des pages HTML. */
|
public Builder htmlStylesTemplate(String template) { this.htmlStylesTemplate = template; return this; }
|
/** Surcharge une variable du template HTML (ex. {@code html.h1}, {@code html.link}). */
|
public Builder htmlVar(String key, String value) { this.htmlVars.put(key, value); return this; }
|
|
public ManuelTheme build() {
|
return new ManuelTheme(this);
|
}
|
}
|
}
|