diff --git a/base/src/main/java/atlantafx/base/theme/NordDark.java b/base/src/main/java/atlantafx/base/theme/NordDark.java new file mode 100755 index 0000000..e2b8d71 --- /dev/null +++ b/base/src/main/java/atlantafx/base/theme/NordDark.java @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.base.theme; + +import java.net.URI; +import java.util.Set; + +public class NordDark extends AbstractTheme { + + public NordDark() {} + + public NordDark(URI... stylesheets) { + super(stylesheets); + } + + public NordDark(Set stylesheets) { + super(stylesheets); + } + + @Override + public String getName() { + return "Nord Dark"; + } + + @Override + public String getUserAgentStylesheet() { + return "/atlantafx/base/theme/nord-dark.css"; + } + + @Override + public boolean isDarkMode() { + return true; + } +} diff --git a/base/src/main/java/atlantafx/base/theme/NordLight.java b/base/src/main/java/atlantafx/base/theme/NordLight.java new file mode 100755 index 0000000..bc4e075 --- /dev/null +++ b/base/src/main/java/atlantafx/base/theme/NordLight.java @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: MIT */ +package atlantafx.base.theme; + +import java.net.URI; +import java.util.Set; + +public class NordLight extends AbstractTheme { + + public NordLight() {} + + public NordLight(URI... stylesheets) { + super(stylesheets); + } + + public NordLight(Set stylesheets) { + super(stylesheets); + } + + @Override + public String getName() { + return "Nord Light"; + } + + @Override + public String getUserAgentStylesheet() { + return "/atlantafx/base/theme/nord-light.css"; + } + + @Override + public boolean isDarkMode() { + return false; + } +} diff --git a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java index 6ed19d4..996639f 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/AbstractPage.java @@ -3,7 +3,6 @@ package atlantafx.sampler.page; import atlantafx.base.controls.Spacer; import atlantafx.base.theme.Styles; -import atlantafx.sampler.theme.HighlightJSTheme; import atlantafx.sampler.theme.ThemeManager; import javafx.event.ActionEvent; import javafx.event.EventHandler; @@ -86,8 +85,8 @@ public abstract class AbstractPage extends BorderPane implements Page { var scrollPane = new ScrollPane(userContentWrapper); setScrollConstraints(scrollPane, - ScrollPane.ScrollBarPolicy.AS_NEEDED, true, - ScrollPane.ScrollBarPolicy.AS_NEEDED, true + ScrollPane.ScrollBarPolicy.AS_NEEDED, true, + ScrollPane.ScrollBarPolicy.AS_NEEDED, true ); scrollPane.setMaxHeight(10_000); @@ -135,10 +134,9 @@ public abstract class AbstractPage extends BorderPane implements Page { Objects.requireNonNull(stream, "Missing source file '" + sourceFileName + "';"); // set syntax highlight theme according to JavaFX theme - var highlightJSTheme = ThemeManager.getInstance().getTheme().isDarkMode() ? - HighlightJSTheme.githubDark() : - HighlightJSTheme.githubLight(); - codeViewer.setContent(stream, highlightJSTheme); + ThemeManager tm = ThemeManager.getInstance(); + System.out.println(tm.getMatchingHighlightJSTheme(tm.getTheme()).getBackground()); + codeViewer.setContent(stream, tm.getMatchingHighlightJSTheme(tm.getTheme())); graphic.setIconCode(ICON_SAMPLE); codeViewerWrapper.toFront(); diff --git a/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java b/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java index f40d637..1edeb8d 100644 --- a/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java +++ b/sampler/src/main/java/atlantafx/sampler/theme/HighlightJSTheme.java @@ -30,4 +30,18 @@ public class HighlightJSTheme { var bg = "#0d1117"; return new HighlightJSTheme(css, bg); } + + public static HighlightJSTheme nordLight() { + // there's no Nord light theme, + // below is "stackoverflow-light" theme with changed background + var css = "*/.hljs{color:#2f3337;background:#ECEFF4}.hljs-subst{color:#2f3337}.hljs-comment{color:#656e77}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#015692}.hljs-attribute{color:#803378}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#b75501}.hljs-selector-class{color:#015692}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#54790d}.hljs-meta,.hljs-selector-pseudo{color:#015692}.hljs-built_in,.hljs-literal,.hljs-title{color:#b75501}.hljs-bullet,.hljs-code{color:#535a60}.hljs-meta .hljs-string{color:#54790d}.hljs-deletion{color:#c02d2e}.hljs-addition{color:#2f6f44}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}"; + var bg = "ECEFF4"; + return new HighlightJSTheme(css, bg); + } + + public static HighlightJSTheme nordDark() { + var css = "pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#2e3440}.hljs,.hljs-subst{color:#d8dee9}.hljs-selector-tag{color:#81a1c1}.hljs-selector-id{color:#8fbcbb;font-weight:700}.hljs-selector-attr,.hljs-selector-class{color:#8fbcbb}.hljs-property,.hljs-selector-pseudo{color:#88c0d0}.hljs-addition{background-color:rgba(163,190,140,.5)}.hljs-deletion{background-color:rgba(191,97,106,.5)}.hljs-built_in,.hljs-class,.hljs-type{color:#8fbcbb}.hljs-function,.hljs-function>.hljs-title,.hljs-title.hljs-function{color:#88c0d0}.hljs-keyword,.hljs-literal,.hljs-symbol{color:#81a1c1}.hljs-number{color:#b48ead}.hljs-regexp{color:#ebcb8b}.hljs-string{color:#a3be8c}.hljs-title{color:#8fbcbb}.hljs-params{color:#d8dee9}.hljs-bullet{color:#81a1c1}.hljs-code{color:#8fbcbb}.hljs-emphasis{font-style:italic}.hljs-formula{color:#8fbcbb}.hljs-strong{font-weight:700}.hljs-link:hover{text-decoration:underline}.hljs-comment,.hljs-quote{color:#4c566a}.hljs-doctag{color:#8fbcbb}.hljs-meta,.hljs-meta .hljs-keyword{color:#5e81ac}.hljs-meta .hljs-string{color:#a3be8c}.hljs-attr{color:#8fbcbb}.hljs-attribute{color:#d8dee9}.hljs-name{color:#81a1c1}.hljs-section{color:#88c0d0}.hljs-tag{color:#81a1c1}.hljs-template-variable,.hljs-variable{color:#d8dee9}.hljs-template-tag{color:#5e81ac}.language-abnf .hljs-attribute{color:#88c0d0}.language-abnf .hljs-symbol{color:#ebcb8b}.language-apache .hljs-attribute{color:#88c0d0}.language-apache .hljs-section{color:#81a1c1}.language-arduino .hljs-built_in{color:#88c0d0}.language-aspectj .hljs-meta{color:#d08770}.language-aspectj>.hljs-title{color:#88c0d0}.language-bnf .hljs-attribute{color:#8fbcbb}.language-clojure .hljs-name{color:#88c0d0}.language-clojure .hljs-symbol{color:#ebcb8b}.language-coq .hljs-built_in{color:#88c0d0}.language-cpp .hljs-meta .hljs-string{color:#8fbcbb}.language-css .hljs-built_in{color:#88c0d0}.language-css .hljs-keyword{color:#d08770}.language-diff .hljs-meta,.language-ebnf .hljs-attribute{color:#8fbcbb}.language-glsl .hljs-built_in{color:#88c0d0}.language-groovy .hljs-meta:not(:first-child),.language-haxe .hljs-meta,.language-java .hljs-meta{color:#d08770}.language-ldif .hljs-attribute{color:#8fbcbb}.language-lisp .hljs-name,.language-lua .hljs-built_in,.language-moonscript .hljs-built_in,.language-nginx .hljs-attribute{color:#88c0d0}.language-nginx .hljs-section{color:#5e81ac}.language-pf .hljs-built_in,.language-processing .hljs-built_in{color:#88c0d0}.language-scss .hljs-keyword,.language-stylus .hljs-keyword{color:#81a1c1}.language-swift .hljs-meta{color:#d08770}.language-vim .hljs-built_in{color:#88c0d0;font-style:italic}.language-yaml .hljs-meta{color:#d08770}"; + var bg = "#2E3440"; + return new HighlightJSTheme(css, bg); + } } diff --git a/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java b/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java index 61429cd..934ad5a 100644 --- a/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java +++ b/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: MIT */ package atlantafx.sampler.theme; -import atlantafx.sampler.Resources; import atlantafx.base.theme.PrimerDark; import atlantafx.base.theme.PrimerLight; import atlantafx.base.theme.Theme; import atlantafx.sampler.Launcher; +import atlantafx.sampler.Resources; import javafx.application.Application; import javafx.scene.Scene; @@ -62,6 +62,14 @@ public final class ThemeManager { Resources.getResource("theme-test/primer-dark.css"), appStylesheets ), true)); + themes.add(new ExternalTheme("Nord Light", DUMMY_STYLESHEET, merge( + Resources.getResource("theme-test/nord-light.css"), + appStylesheets + ), false)); + themes.add(new ExternalTheme("Nord Dark", DUMMY_STYLESHEET, merge( + Resources.getResource("theme-test/nord-dark.css"), + appStylesheets + ), true)); } else { themes.add(new PrimerLight(appStylesheets)); themes.add(new PrimerDark(appStylesheets)); @@ -86,6 +94,13 @@ public final class ThemeManager { currentFontSize = fontSize; } + public HighlightJSTheme getMatchingHighlightJSTheme(Theme theme) { + Objects.requireNonNull(theme); + if ("Nord Light".equals(theme.getName())) { return HighlightJSTheme.nordLight(); } + if ("Nord Dark".equals(theme.getName())) { return HighlightJSTheme.nordDark(); } + return theme.isDarkMode() ? HighlightJSTheme.githubDark() : HighlightJSTheme.githubLight(); + } + @SafeVarargs private Set merge(T first, T... arr) { var set = new LinkedHashSet(); diff --git a/styles/pom.xml b/styles/pom.xml index e1c8a6d..9659909 100755 --- a/styles/pom.xml +++ b/styles/pom.xml @@ -26,6 +26,8 @@ ${scss.inputDir}/primer-light.scss:${css.outputDir}/primer-light.css ${scss.inputDir}/primer-dark.scss:${css.outputDir}/primer-dark.css + ${scss.inputDir}/nord-light.scss:${css.outputDir}/nord-light.css + ${scss.inputDir}/nord-dark.scss:${css.outputDir}/nord-dark.css --no-source-map diff --git a/styles/src/nord-dark.scss b/styles/src/nord-dark.scss new file mode 100755 index 0000000..86da405 --- /dev/null +++ b/styles/src/nord-dark.scss @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT + +// Nord color palette +// informational, not commented to display color preview +$nord0: #2E3440; +$nord1: #3B4252; +$nord1: #434C5E; +$nord3: #4C566A; +$nord4: #D8DEE9; +$nord5: #E5E9F0; +$nord6: #ECEFF4; +$nord7: #8FBCBB; +$nord8: #88C0D0; +$nord9: #81A1C1; +$nord10: #5E81AC; +$nord11: #BF616A; +$nord12: #D08770; +$nord13: #EBCB8B; +$nord14: #A3BE8C; +$nord16: #B48EAD; + +@forward "settings/palette" with ( + + $fg-default: #ECEFF4, + $fg-muted: #DFE4ED, + $fg-subtle: #CFD7E3, + $fg-onEmphasis: #ECEFF4, + + $canvas-default: #2E3440, + $canvas-overlay: #2E3440, + $canvas-inset: #3B4252, + $canvas-subtle: #3B4252, + + $border-default: #4C566A, + $border-muted: #40495A, + $border-subtle: #3B4353, + + $neutral-emphasisPlus: #6e7681, + $neutral-emphasis: #6e7681, + $neutral-muted: rgba(110, 118, 129, 0.4), + $neutral-subtle: rgba(110, 118, 129, 0.1), + + $accent-fg: #81A1C1, + $accent-emphasis: #5E81AC, + $accent-muted: rgba(81, 109, 157, 0.3), + $accent-subtle: rgba(81, 109, 157, 0.1), + + $success-fg: #A3BE8C, + $success-emphasis: #5A7C4F, + $success-muted: rgba(163, 190, 140, 0.4), + $success-subtle: rgba(163, 190, 140, 0.15), + + $warning-fg: #D08770, + $warning-emphasis: #AA5338, + $warning-muted: rgba(170, 83, 56, 0.4), + $warning-subtle: rgba(170, 83, 56, 0.15), + + $danger-fg: #CA7C83, + $danger-emphasis: #BF616A, + $danger-muted: rgba(178, 72, 82, 0.4), + $danger-subtle: rgba(178, 72, 82, 0.15) +); + +@forward "settings/config" with ( + $darkMode: true, + $color-delta-hover: 15%, + $color-delta-active: 20% +); + +@use "general"; + +@forward "components/slider" as slider-* with ( + $thumb-color: -color-fg-subtle, + $thumb-color-border: -color-fg-subtle +); + +@forward "components/titled-pane" as titled-pane-* with ( + $elevation-color: -color-bg-inset +); + +@forward "components/tooltip" as tooltip-* with ( + $color-bg: -color-bg-inset, + $color-fg: -color-fg-default +); + +@use "components"; diff --git a/styles/src/nord-light.scss b/styles/src/nord-light.scss new file mode 100755 index 0000000..07221e6 --- /dev/null +++ b/styles/src/nord-light.scss @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT + +// Nord is officially only supports dark theme: +// https://github.com/arcticicestudio/nord/issues/46 + +// Nord color palette +// informational, not commented to display color preview +$nord0: #2E3440; +$nord1: #3B4252; +$nord1: #434C5E; +$nord3: #4C566A; +$nord4: #D8DEE9; +$nord5: #E5E9F0; +$nord6: #ECEFF4; +$nord7: #8FBCBB; +$nord8: #88C0D0; +$nord9: #81A1C1; +$nord10: #5E81AC; +$nord11: #BF616A; +$nord12: #D08770; +$nord13: #EBCB8B; +$nord14: #A3BE8C; +$nord16: #B48EAD; + +@forward "settings/palette" with ( + + $fg-default: #2E3440, + $fg-muted: #4C566A, + $fg-subtle: #5A657C, + $fg-onEmphasis: #ECEFF4, + + $canvas-default: #ECEFF4, + $canvas-overlay: #ECEFF4, + $canvas-inset: #D8DEE9, + $canvas-subtle: #E5E9F0, + + $border-default: rgba(181, 188, 201, 1), + $border-muted: rgba(181, 188, 201, 0.75), + $border-subtle: rgba(181, 188, 201, 0.6), + + $neutral-emphasisPlus: #24292f, + $neutral-emphasis: #6e7781, + $neutral-muted: rgba(175, 184, 193, 0.2), + $neutral-subtle: rgba(234, 238, 242, 0.5), + + $accent-fg: #5E81AC, + $accent-emphasis: #5E81AC, + $accent-muted: rgba(81, 109, 157, 0.3), + $accent-subtle: rgba(81, 109, 157, 0.1), + + $success-fg: #67864c, + $success-emphasis: #67864c, + $success-muted: rgba(103, 134, 76, 0.4), + $success-subtle: rgba(103, 134, 76, 0.15), + + $warning-fg: #C26446, + $warning-emphasis: #C26446, + $warning-muted: rgba(170, 83, 56, 0.4), + $warning-subtle: rgba(170, 83, 56, 0.15), + + $danger-fg: #BF616A, + $danger-emphasis: #BF616A, + $danger-muted: rgba(178, 72, 82, 0.4), + $danger-subtle: rgba(178, 72, 82, 0.15) +); + +@forward "settings/config" with ( + $color-delta-hover: 5%, + $color-delta-active: 10% +); + +@use "general"; + +@use "components";