Browse Source

Migrate to Nokee plugin (#173)

Migrate to Nokee plugin.
Improved dark mode detection on macOS.
Fixed issue where the native theme option wasn't available in the theme settings on macOS.
Improved scrollbars.
pull/188/head
Jannis Weis 5 years ago committed by GitHub
parent
commit
deae2142da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .github/workflows/gradle.yml
  2. 14
      .github/workflows/libs.yml
  3. 24
      build.gradle.kts
  4. 20
      buildSrc/build.gradle.kts
  5. 14
      buildSrc/src/main/groovy/CallableLogger.groovy
  6. 351
      buildSrc/src/main/groovy/DownloadPrebuiltBinaryFromGitHubAction.groovy
  7. 31
      buildSrc/src/main/groovy/JniUtils.groovy
  8. 45
      buildSrc/src/main/groovy/OneTimeLogger.groovy
  9. 66
      buildSrc/src/main/groovy/UberJniJarPlugin.groovy
  10. 164
      buildSrc/src/main/groovy/UsePrebuiltBinariesWhenUnbuildablePlugin.groovy
  11. 18
      buildSrc/src/main/kotlin/MacOSSdkPathTask.kt
  12. 126
      buildSrc/src/main/kotlin/jni-library.gradle.kts
  13. 21
      core/build.gradle.kts
  14. 2
      core/src/main/java/com/github/weisj/darklaf/settings/ThemeSettingsPanel.java
  15. 18
      core/src/main/java/com/github/weisj/darklaf/ui/scrollpane/DarkMacScrollBarUI.java
  16. 19
      core/src/main/java/com/github/weisj/darklaf/ui/scrollpane/DarkScrollBarUI.java
  17. 3
      core/src/main/resources/com/github/weisj/darklaf/properties/platform/mac.properties
  18. 9
      core/src/main/resources/com/github/weisj/darklaf/properties/ui/scrollBar.properties
  19. 2
      core/src/test/java/ui/ComponentDemo.java
  20. 1
      core/src/test/java/ui/PreferenceChangeDemo.java
  21. 1
      gradle.properties
  22. 79
      macos/build.gradle.kts
  23. 1
      macos/gradle.properties
  24. 0
      macos/pre-build-libraries/macos-x86-64/library.md
  25. 4
      macos/src/main/java/com/github/weisj/darklaf/platform/macos/JNIThemeInfoMacOS.java
  26. 13
      macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSLibrary.java
  27. 13
      macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSThemePreferenceProvider.java
  28. 0
      macos/src/main/objcpp/Decorations.mm
  29. 66
      macos/src/main/objcpp/ThemeInfo.mm
  30. 20
      native-utils/src/main/java/com/github/weisj/darklaf/platform/AbstractLibrary.java
  31. 1
      settings.gradle.kts
  32. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/darcula/darcula_defaults.properties
  33. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_dark/high_contrast_dark_defaults.properties
  34. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_dark/high_contrast_dark_ui.properties
  35. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_light/high_contrast_light_defaults.properties
  36. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_light/high_contrast_light_ui.properties
  37. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/intellij/intellij_defaults.properties
  38. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/one_dark/one_dark_defaults.properties
  39. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/one_dark/one_dark_ui.properties
  40. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/solarized_dark/solarized_dark_defaults.properties
  41. 2
      theme/src/main/resources/com/github/weisj/darklaf/theme/solarized_light/solarized_light_defaults.properties
  42. 57
      windows/build.gradle.kts
  43. 0
      windows/pre-build-libraries/windows-x86-64/library.md
  44. 0
      windows/pre-build-libraries/windows-x86/library.md
  45. 13
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsLibrary.java

6
.github/workflows/gradle.yml

@ -44,7 +44,7 @@ jobs:
with: with:
java-version: 11 java-version: 11
- name: Build - name: Build
run: ./gradlew build -PskipAutostyle run: ./gradlew build -PskipAutostyle --info
linux: linux:
name: Linux (Java 8) name: Linux (Java 8)
@ -58,7 +58,7 @@ jobs:
with: with:
java-version: 8 java-version: 8
- name: Build - name: Build
run: ./gradlew build -PskipAutostyle run: ./gradlew build -PskipAutostyle --info
macos: macos:
name: macOS (Java 11) name: macOS (Java 11)
@ -72,4 +72,4 @@ jobs:
with: with:
java-version: 11 java-version: 11
- name: Build - name: Build
run: ./gradlew build -PskipAutostyle run: ./gradlew build -PskipAutostyle --info

14
.github/workflows/libs.yml

@ -55,13 +55,13 @@ jobs:
- name: Upload x86 artifact - name: Upload x86 artifact
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: darklaf-windows_x86.dll name: windows-x86
path: windows/build/lib/main/release/x86/darklaf-windows.dll path: windows/build/libs/main/x86/darklaf-windows.dll
- name: Upload x86-64 artifact - name: Upload x86-64 artifact
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: darklaf-windows_x86-64.dll name: windows-x86-64
path: windows/build/lib/main/release/x86-64/darklaf-windows.dll path: windows/build/libs/main/x86-64/darklaf-windows.dll
macOS: macOS:
name: macOS (Java 11) name: macOS (Java 11)
@ -79,5 +79,7 @@ jobs:
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: libdarklaf-macos.dylib name: macos-x86-64
path: macos/build/lib/main/release/stripped/libdarklaf-macos.dylib path: macos/build/libs/main/libdarklaf-macos.dylib
- name: Print library information
run: otool -l macos/build/libs/main/libdarklaf-macos.dylib

24
build.gradle.kts

@ -19,8 +19,9 @@ val enableGradleMetadata by props()
val skipAutostyle by props() val skipAutostyle by props()
val String.v: String get() = rootProject.extra["$this.version"] as String val String.v: String get() = rootProject.extra["$this.version"] as String
val projectVersion = "darklaf".v
val buildVersion = "darklaf".v + releaseParams.snapshotSuffix val buildVersion = projectVersion + releaseParams.snapshotSuffix
println("Building: Darklaf $buildVersion") println("Building: Darklaf $buildVersion")
println(" JDK: " + System.getProperty("java.home")) println(" JDK: " + System.getProperty("java.home"))
@ -83,6 +84,24 @@ allprojects {
mavenCentral() mavenCentral()
} }
val githubAccessToken by props("")
plugins.withType<UsePrebuiltBinariesWhenUnbuildablePlugin> {
prebuildBinaries {
prebuildLibrariesFolder = "pre-build-libraries"
missingLibraryIsFailure = false
github {
user = "weisj"
repository = "darklaf"
workflow = "libs.yml"
branches = listOf("master", "v$projectVersion", projectVersion)
accessToken = githubAccessToken
manualDownloadUrl =
"https://github.com/weisJ/darklaf/actions?query=workflow%3A%22Build+Native+Libraries%22+is%3Asuccess+branch%3Amaster"
}
}
}
if (!skipAutostyle) { if (!skipAutostyle) {
apply(plugin = "com.github.autostyle") apply(plugin = "com.github.autostyle")
autostyle { autostyle {
@ -266,7 +285,8 @@ allprojects {
configure<PublishingExtension> { configure<PublishingExtension> {
if (project.path.startsWith(":darklaf-dependencies-bom") || if (project.path.startsWith(":darklaf-dependencies-bom") ||
project.path == ":") { project.path == ":"
) {
// We don't it to Central for now // We don't it to Central for now
return@configure return@configure
} }

20
buildSrc/build.gradle.kts

@ -1,16 +1,28 @@
plugins { plugins {
`kotlin-dsl` `java-gradle-plugin`
groovy
} }
dependencies { dependencies {
implementation(kotlin("gradle-plugin")) implementation(platform("dev.nokee:nokee-gradle-plugins:0.4.0-60cff2b"))
} }
repositories { repositories {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven { url = uri("https://dl.bintray.com/nokeedev/distributions") }
maven { url = uri("https://dl.bintray.com/nokeedev/distributions-snapshots") }
} }
configure<KotlinDslPluginOptions> { gradlePlugin {
experimentalWarning.set(false) plugins {
create("uber-jni-jar") {
id = "uber-jni-jar"
implementationClass = "UberJniJarPlugin"
}
create("use-prebuilt-binaries") {
id = "use-prebuilt-binaries"
implementationClass = "UsePrebuiltBinariesWhenUnbuildablePlugin"
}
}
} }

14
buildSrc/src/main/groovy/CallableLogger.groovy

@ -0,0 +1,14 @@
import java.util.concurrent.Callable
class CallableLogger extends OneTimeLogger implements Callable<List<File>> {
CallableLogger(Runnable logger) {
super(logger)
}
@Override
List<File> call() throws Exception {
super.log()
return Collections.emptyList()
}
}

351
buildSrc/src/main/groovy/DownloadPrebuiltBinaryFromGitHubAction.groovy

@ -0,0 +1,351 @@
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.transform.CompileStatic
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Transformer
import org.gradle.api.tasks.OutputFile
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.util.concurrent.locks.ReadWriteLock
import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.stream.Stream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
@CompileStatic
class DownloadPrebuiltBinaryFromGitHubAction extends DefaultTask {
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock()
private static final String VERSION_INFO_FILE_NAME = "github_artifact_versions.json"
private static final String TEMP_PATH = "tmp${File.separator}prebuild"
private static final String PRE_BUILD_PATH = "libs${File.separator}prebuild"
private final OneTimeLogger tokenWarning = new OneTimeLogger.Static({
error("""No github access token is specified. Latest artifacts will need to be included manually.
|The access token needs to have the 'read-public' property. Specify using:
| -PgithubAccessToken=<your token>
|or by setting
| githubAccessToken=<your token>
|inside the gradle.properties file.
|""".stripMargin())
}, "missingTokenWarning")
private final OneTimeLogger useCachedWarning = new OneTimeLogger({
log("Could not download artifact or artifact information. Using cached version")
})
private Map cacheInfo
private String manualDownloadUrl = ""
private String user
private String repository
private String workflow
private List<String> branches = []
private boolean missingLibraryIsFailure
private String githubAccessToken
private String variant
private Optional<File> prebuiltBinary
@OutputFile
File getPrebuiltBinaryFile() {
if (user == null) throw new GradleException("Github user isn't specified")
if (repository == null) repository = project.name
if (workflow == null) throw new GradleException("Workflow isn't specified")
if (prebuiltBinary == null) {
if (githubAccessToken == null || githubAccessToken.isEmpty()) {
tokenWarning.log()
}
prebuiltBinary = getExternalBinary(variant)
}
return prebuiltBinary.orElseGet {
String errorMessage = """Library for $variant could not be downloaded.
|Download it from $manualDownloadUrl
|""".stripMargin()
if (missingLibraryIsFailure) {
throw new GradleException(format(errorMessage))
} else {
new OneTimeLogger.Static({
error(errorMessage)
}, "${variant}-missing").log()
}
return createDirectory(tempFilePath("dummy/"))
}
}
void setMissingLibraryIsFailure(boolean missingLibraryIsFailure) {
this.missingLibraryIsFailure = missingLibraryIsFailure
}
void setGithubAccessToken(String githubAccessToken) {
this.githubAccessToken = githubAccessToken
}
void setVariant(String variant) {
this.variant = variant
}
void setUser(String user) {
this.user = user
}
void setRepository(String repository) {
this.repository = repository
}
void setWorkflow(String workflow) {
this.workflow = workflow
}
void setManualDownloadUrl(String manualDownloadUrl) {
this.manualDownloadUrl = manualDownloadUrl
}
void setBranches(List<String> branches) {
this.branches = branches
}
private Map getCacheInfo() {
if (cacheInfo == null) {
LOCK.readLock().lock()
try {
File cacheInfoFile = getCacheInfoFile()
JsonSlurper jsonParser = new JsonSlurper()
cacheInfo = jsonParser.parseText(cacheInfoFile.text) as Map
} finally {
LOCK.readLock().unlock()
}
}
return cacheInfo
}
private File getCacheInfoFile() {
String path = preBuildPath(VERSION_INFO_FILE_NAME)
File cacheInfo = new File(path)
if (!cacheInfo.exists()) {
cacheInfo = createFile(path)
cacheInfo << "{}"
}
return cacheInfo
}
private void writeToCache(String variantName, String timeStamp, File file) {
LOCK.writeLock().lock()
try {
Map cacheInfo = getCacheInfo()
Map entry = [timeStamp: timeStamp, path: file.absolutePath]
cacheInfo.put(variantName, entry)
getCacheInfoFile().write(JsonOutput.prettyPrint(JsonOutput.toJson(cacheInfo)))
} finally {
}
}
Optional<File> getExternalBinary(String variant) {
Tuple2<Optional<DownloadInfo>, Optional<File>> fetchResult = getBinaryDownloadUrl(variant)
Optional<DownloadInfo> downloadInfo = fetchResult.getFirst()
Optional<File> cachedFile = fetchResult.getSecond()
if (cachedFile.isPresent()) {
log("Reusing previously downloaded binary ${cachedFile.map { it.absolutePath }.orElse(null)}")
return cachedFile
}
Optional<File> downloadedFile = downloadInfo.map {
getBinaryFromUrl(variant, it.url).orElse(null)
}
if (downloadedFile.isPresent()) {
writeToCache(variant, downloadInfo.get()?.timeStamp, downloadedFile.get())
} else {
info("No file found for variant $variant")
}
if (downloadedFile.isPresent()) return downloadedFile
return getCachedFile(variant)
}
private Optional<File> getBinaryFromUrl(String variant, String url) {
File directory = createDirectory(preBuildPath(variant))
info("Downloading binary for variant '$variant' from $url")
Optional<File> file = downloadZipFile(url, variant).map { unzip(it, directory).findFirst() }.orElse(Optional.empty())
info("Finished download for variant '$variant'")
return file
}
private String preBuildPath(String variant) {
return "${project.buildDir}${File.separator}$PRE_BUILD_PATH${File.separator}$variant"
}
private Optional<ZipFile> downloadZipFile(String url, String variant) {
return fetch(url) {
File file = createFile(zipPath(variant))
Path response = file.toPath()
Files.copy(it.getInputStream(), response, StandardCopyOption.REPLACE_EXISTING)
return new ZipFile(file)
}
}
private String zipPath(String name) {
return tempFilePath("${name}.zip")
}
private String tempFilePath(String name) {
return "$project.buildDir${File.separator}$TEMP_PATH${File.separator}${name}"
}
private static Stream<File> unzip(ZipFile self, File directory) {
Collection<ZipEntry> files = self.entries().findAll { !(it as ZipEntry).directory }
return files.stream().map {
ZipEntry e = it as ZipEntry
e.name.with { fileName ->
File outputFile = createFile("${directory.path}$File.separator$fileName")
Files.copy(self.getInputStream(e), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
return outputFile
}
}
}
private Tuple2<Optional<DownloadInfo>, Optional<File>> getBinaryDownloadUrl(String variantName) {
boolean isUptoDate = false
File cachedFile = null
String timeStamp = null
String artifactUrl = getLatestRun(getJson(getWorkflowsUrl())).with {
timeStamp = it.get("created_at")
Optional<String> cachedFilePath = getCachedFilePath(variantName, timeStamp)
isUptoDate = cachedFilePath.isPresent()
if (isUptoDate) {
cachedFile = new File(cachedFilePath.get())
isUptoDate = cachedFile.exists()
}
return get("artifacts_url") as String
}
info("Latest artifact for variant '$variantName' is from $timeStamp")
if (isUptoDate) {
return new Tuple2<>(Optional.empty(), Optional.of(cachedFile))
}
DownloadInfo downloadInfo = artifactUrl?.with { url ->
Map[] artifacts = getJson(url).get("artifacts") as Map[]
String artifactDownloadUrl = artifacts?.find { variantName == it.get("name") }?.get("url") as String
return artifactDownloadUrl?.with {
new DownloadInfo(getJson(it)?.get("archive_download_url") as String, timeStamp)
}
}
return new Tuple2<>(Optional.ofNullable(downloadInfo), Optional.empty())
}
private String getWorkflowsUrl() {
return "https://api.github.com/repos/$user/$repository/actions/workflows/$workflow/runs"
}
private Optional<String> getCachedFilePath(String variantName, String timeStamp) {
Map cacheInfo = getCacheInfo()
boolean isLatest = (cacheInfo[variantName] as Map)?.get("timeStamp") == timeStamp
if (isLatest) {
return Optional.ofNullable((cacheInfo[variantName] as Map)?.get("path") as String)
} else {
return Optional.empty()
}
}
private Optional<File> getCachedFile(String variant) {
Map cacheInfo = getCacheInfo()
return Optional.ofNullable(cacheInfo[variant] as Map).map {
return new File(String.valueOf(it["path"])).with {
if (it.exists()) useCachedWarning.log()
it.exists() ? it : null
}
}
}
private Map getLatestRun(Map json) {
Map[] runs = json.get("workflow_runs") as Map[]
return Optional.ofNullable(runs?.find { run ->
boolean completed = "completed" == run.get("status")
boolean success = "success" == run.get("conclusion")
boolean isCorrectBranch = branches.isEmpty() || branches.contains(run.get("head_branch")?.toString())
return completed && success && isCorrectBranch
}).orElseGet {
log("No suitable workflow run found.")
return Collections.emptyMap()
}
}
private Map getJson(String url) {
return fetch(url) {
JsonSlurper jsonParser = new JsonSlurper()
Map parsedJson = jsonParser.parseText(it.getInputStream().getText()) as Map
return parsedJson
}.orElse(Collections.emptyMap())
}
private <T> Optional<T> fetch(String url, Transformer<T, HttpURLConnection> transformer) {
info("Fetching $url")
if (isOffline()) return Optional.empty()
HttpURLConnection get = new URL(url).openConnection() as HttpURLConnection
get.setRequestMethod("GET")
githubAccessToken?.with {
get.setRequestProperty("Authorization", "token $it")
}
try {
def responseCode = get.getResponseCode()
if (responseCode == HttpURLConnection.HTTP_OK) {
return Optional.ofNullable(transformer.transform(get))
} else {
log("Could not fetch $url. Response code '$responseCode'.")
}
} catch (IOException ignored) {
}
return Optional.empty()
}
private static File createFile(String fileName) {
File file = new File(fileName)
if (file.exists()) file.delete()
file.getParentFile().mkdirs()
file.createNewFile()
return file
}
private static File createDirectory(String fileName) {
File file = new File(fileName)
file.mkdirs()
return file
}
private boolean isOffline() {
return project.getGradle().startParameter.isOffline()
}
private void info(String message) {
project.logger.info(format(message))
}
private void log(String message) {
project.logger.warn(format(message))
}
private void error(String message) {
project.logger.error(format(message))
}
private String format(String message) {
String pad = " " * (project.name.size() + 2)
return "${project.name}: ${message.replace("\n", "\n$pad")}"
}
private class DownloadInfo {
protected String url
protected String timeStamp
private DownloadInfo(String url, String timeStamp) {
this.url = url
this.timeStamp = timeStamp
}
}
}

31
buildSrc/src/main/groovy/JniUtils.groovy

@ -0,0 +1,31 @@
import dev.nokee.platform.nativebase.OperatingSystemFamily
import dev.nokee.platform.nativebase.TargetMachine
import org.gradle.api.GradleException
import org.gradle.api.Project
class JniUtils {
static String asVariantName(TargetMachine targetMachine) {
String operatingSystemFamily = 'macos'
if (targetMachine.operatingSystemFamily.windows) {
operatingSystemFamily = 'windows'
}
String architecture = 'x86-64'
if (targetMachine.architecture.'32Bit') {
architecture = 'x86'
}
return "$operatingSystemFamily-$architecture"
}
static String getLibraryFileNameFor(Project project, OperatingSystemFamily osFamily) {
if (osFamily.windows) {
return "${project.name}.dll"
} else if (osFamily.linux) {
return "lib${project.name}.so"
} else if (osFamily.macOS) {
return "lib${project.name}.dylib"
}
throw new GradleException("Unknown operating system family '${osFamily}'.")
}
}

45
buildSrc/src/main/groovy/OneTimeLogger.groovy

@ -0,0 +1,45 @@
class OneTimeLogger {
private final Runnable logger
private boolean messageAlreadyLogged = false
OneTimeLogger(Runnable logger) {
this.logger = logger
}
protected boolean isLogged() {
return messageAlreadyLogged
}
protected void setLogged(boolean logged) {
messageAlreadyLogged = logged
}
protected void log() {
if (!isLogged()) {
logger.run()
setLogged(true)
}
}
static class Static extends OneTimeLogger {
private static final Map<Object, Boolean> isLogged = new HashMap<>()
private final Object identifier
Static(Runnable logger, Object identifier) {
super(logger)
this.identifier = identifier
}
@Override
protected void setLogged(boolean logged) {
isLogged.put(identifier, logged)
}
@Override
protected boolean isLogged() {
return Boolean.TRUE == isLogged.get(identifier)
}
}
}

66
buildSrc/src/main/groovy/UberJniJarPlugin.groovy

@ -0,0 +1,66 @@
import dev.nokee.platform.jni.JniJarBinary
import dev.nokee.platform.jni.JniLibrary
import dev.nokee.platform.jni.JniLibraryExtension
import dev.nokee.platform.nativebase.TargetMachine
import groovy.transform.CompileStatic
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Transformer
import org.gradle.api.file.CopySpec
import org.gradle.api.provider.Provider
import org.gradle.jvm.tasks.Jar
@CompileStatic
class UberJniJarPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.tasks.named('jar', Jar) { task ->
configure(task)
}
}
private static void configure(Jar task) {
def project = task.getProject()
def logger = task.getLogger()
def library = project.extensions.getByType(JniLibraryExtension)
library.binaries.withType(JniJarBinary).configureEach {
if (it.jarTask.isPresent()) it.jarTask.get()?.enabled = false
}
if (library.targetMachines.get().size() >= 1) {
logger.info("${project.name}: Merging binaries into the JVM Jar.")
for (TargetMachine targetMachine : library.targetMachines.get()) {
Provider<JniLibrary> variant = library.variants
.flatMap(targetMachineOf(targetMachine))
.map(onlyOne() as Transformer<?, ? super List<?>>) as Provider<JniLibrary>
task.into(variant.map { it.resourcePath }) { CopySpec spec ->
spec.from(variant.map { it.nativeRuntimeFiles })
}
}
}
}
// Filter variants that match the specified target machine.
private static Transformer<Iterable<JniLibrary>, JniLibrary> targetMachineOf(TargetMachine targetMachine) {
return new Transformer<Iterable<JniLibrary>, JniLibrary>() {
@Override
Iterable<JniLibrary> transform(JniLibrary variant) {
if (variant.targetMachine == targetMachine) {
return [variant]
}
return []
}
}
}
// Ensure only a single variant is present in the collection and return the variant.
private static Transformer<JniLibrary, List<? extends JniLibrary>> onlyOne() {
return new Transformer<JniLibrary, List<? extends JniLibrary>>() {
@Override
JniLibrary transform(List<? extends JniLibrary> variants) {
assert variants.size() == 1
return variants.first()
}
}
}
}

164
buildSrc/src/main/groovy/UsePrebuiltBinariesWhenUnbuildablePlugin.groovy

@ -0,0 +1,164 @@
import dev.nokee.platform.jni.JniLibraryExtension
import groovy.transform.CompileStatic
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
@CompileStatic
class UsePrebuiltBinariesWhenUnbuildablePlugin implements Plugin<Project> {
private PrebuildBinariesExtension prebuildExtension
private GithubArtifactExtension githubArtifactExtension
void prebuildBinaries(Action<? extends PrebuildBinariesExtension> action) {
action.execute(prebuildExtension)
}
PrebuildBinariesExtension getPrebuildBinaries() {
return prebuildExtension
}
@Override
void apply(Project project) {
JniLibraryExtension library = project.extensions.getByType(JniLibraryExtension)
prebuildExtension = project.extensions.create("prebuildBinaries", PrebuildBinariesExtension, this)
githubArtifactExtension = (prebuildExtension as ExtensionAware).with {
it.extensions.create("github", GithubArtifactExtension)
}
library.variants.configureEach { var ->
if (prebuildExtension.alwaysUsePrebuildArtifact || !var.sharedLibrary.buildable) {
// Try to include the library file... if available
def defaultLibraryName = JniUtils.getLibraryFileNameFor(project, var.targetMachine.operatingSystemFamily)
def variantName = JniUtils.asVariantName(var.targetMachine)
def libraryFile = project.file(
"${prebuildExtension.prebuildLibrariesFolder}/$variantName/$defaultLibraryName"
)
if (!libraryFile.exists()) {
// No local binary provided. Try to download it from github actions.
def prebuiltBinariesTask = project.tasks.register("downloadPrebuiltBinary$variantName", DownloadPrebuiltBinaryFromGitHubAction.class)
prebuiltBinariesTask.configure {
it.githubAccessToken = githubArtifactExtension.accessToken
it.variant = variantName
it.user = githubArtifactExtension.user
it.repository = githubArtifactExtension.repository
it.workflow = githubArtifactExtension.workflow
it.manualDownloadUrl = githubArtifactExtension.manualDownloadUrl
it.branches = githubArtifactExtension.branches
it.missingLibraryIsFailure = prebuildExtension.missingLibraryIsFailure
}
var.nativeRuntimeFiles.setFrom(prebuiltBinariesTask.map { it.prebuiltBinaryFile })
var.nativeRuntimeFiles.from(new CallableLogger({
project.logger.warn("${project.name}: Using pre-build library from github for targetMachine $variantName.")
}))
} else {
//Use provided library.
var.nativeRuntimeFiles.setFrom(libraryFile)
var.nativeRuntimeFiles.from(new CallableLogger({
def relativePath = project.rootProject.relativePath(libraryFile)
project.logger.warn("${project.name}: Using pre-build library $relativePath for targetMachine $variantName.")
}))
}
}
}
}
static class PrebuildBinariesExtension {
private String prebuildLibrariesFolder = "pre-build-libraries"
private boolean alwaysUsePrebuildArtifact = false
private boolean missingLibraryIsFailure = true
private UsePrebuiltBinariesWhenUnbuildablePlugin plugin
PrebuildBinariesExtension(UsePrebuiltBinariesWhenUnbuildablePlugin plugin) {
this.plugin = plugin
}
void github(Action<? extends GithubArtifactExtension> action) {
action.execute(plugin.githubArtifactExtension)
}
void setAlwaysUsePrebuildArtifact(boolean alwaysUsePrebuildArtifact) {
this.alwaysUsePrebuildArtifact = alwaysUsePrebuildArtifact
}
boolean getAlwaysUsePrebuildArtifact() {
return alwaysUsePrebuildArtifact
}
String getPrebuildLibrariesFolder() {
return prebuildLibrariesFolder
}
boolean getMissingLibraryIsFailure() {
return missingLibraryIsFailure
}
void setPrebuildLibrariesFolder(String prebuildLibrariesFolder) {
this.prebuildLibrariesFolder = prebuildLibrariesFolder
}
void setMissingLibraryIsFailure(boolean missingLibraryIsFailure) {
this.missingLibraryIsFailure = missingLibraryIsFailure
}
}
static class GithubArtifactExtension {
private String user
private String repository
private String workflow
private String manualDownloadUrl
private String accessToken
private List<String> branches = ["master"]
String getUser() {
return user
}
String getRepository() {
return repository
}
String getWorkflow() {
return workflow
}
String getManualDownloadUrl() {
return manualDownloadUrl
}
String getAccessToken() {
return accessToken
}
List<String> getBranches() {
return branches
}
void setUser(String user) {
this.user = user
}
void setRepository(String repository) {
this.repository = repository
}
void setWorkflow(String workflow) {
this.workflow = workflow
}
void setManualDownloadUrl(String manualDownloadUrl) {
this.manualDownloadUrl = manualDownloadUrl
}
void setAccessToken(String accessToken) {
this.accessToken = accessToken
}
void setBranches(List<String> branches) {
this.branches = branches
}
}
}

18
buildSrc/src/main/kotlin/MacOSSdkPathTask.kt

@ -1,18 +0,0 @@
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.gradle.nativeplatform.toolchain.internal.xcode.MacOSSdkPathLocator
import java.io.File
import javax.inject.Inject
open class MacOSSdkPathTask @Inject constructor(
private val locator: MacOSSdkPathLocator
) : DefaultTask() {
@Internal
lateinit var sdkPath: File
@TaskAction
fun run() {
sdkPath = locator.find()
}
}

126
buildSrc/src/main/kotlin/jni-library.gradle.kts

@ -1,126 +0,0 @@
import org.gradle.internal.jvm.Jvm
import org.gradle.kotlin.dsl.invoke
plugins {
`cpp-library`
`java-library`
}
// This configuration might be used for adding cpp-only dependencies
val jniImplementation by configurations.creating
val defaultLibraryName: String by project
configurations.matching {
it.name.startsWith("cppCompile") ||
it.name.startsWith("nativeLink") ||
it.name.startsWith("nativeRuntime")
}.all {
extendsFrom(jniImplementation)
}
tasks.compileJava {
options.headerOutputDirectory.convention(
project.layout.buildDirectory.dir("generated/jni-headers")
)
// The nested output is not marked automatically as an output of the task regarding task dependencies.
// So we mark it manually here.
// See https://github.com/gradle/gradle/issues/6619.
outputs.dir(options.headerOutputDirectory)
// Cannot do incremental header generation, since the pattern for cleaning them up is currently wrong.
// See https://github.com/gradle/gradle/issues/12084.
options.isIncremental = false
}
tasks.withType<CppCompile>().configureEach {
includes(tasks.compileJava.flatMap { it.options.headerOutputDirectory })
}
library {
binaries.configureEach {
val targetOs = targetMachine.operatingSystemFamily
compileTask.get().apply {
val javaHome = Jvm.current().javaHome.canonicalPath
includes("$javaHome/include")
includes(when {
targetOs.isMacOs -> listOf("$javaHome/include/darwin")
targetOs.isLinux -> listOf("$javaHome/include/linux")
targetOs.isWindows -> listOf("$javaHome/include/win32")
else -> emptyList()
})
}
}
}
/**
* Gradle does not support [Provider] for [JavaForkOptions.systemProperty], so
* we pass an object that overrides [Any.toString].
*/
fun Provider<String>.overrideToString() = object {
override fun toString() = orNull ?: ""
}
val TargetMachine.variantName: String get() = "$operatingSystemFamily-$architecture"
// Gradle populates library.binaries in afterEvaluate, so we can't access it earlier
afterEvaluate {
// C++ library is built for Windows/macOS only, so we skip it otherwise
library.developmentBinary.orNull?.let { it as CppSharedLibrary }?.let { developmentBinary ->
tasks.test {
dependsOn(developmentBinary.linkTask)
val libraryDir = developmentBinary.runtimeFile
.map { it.asFile.parentFile.absolutePath }
systemProperty("java.library.path", libraryDir.overrideToString())
}
}
tasks.jar {
//Disable all task. Tasks are reenabled if needed.
library.binaries.get().forEach {
it.compileTask.get().enabled = false
}
library.binaries.get()
.filter { it.isOptimized }
.filterIsInstance<CppSharedLibrary>().let {
val taskMap = it.map { binary -> binary.targetPlatform.targetMachine to binary }.toMap()
library.targetMachines.get().forEach { targetMachine ->
val libraryPath = "com/github/weisj/darklaf/platform/${project.name}"
val variantName = targetMachine.variantName
val libraryFile = file("libraries/$variantName/$defaultLibraryName")
val relativePath = rootProject.relativePath(libraryFile)
when {
libraryFile.exists() -> {
//Use provided library.
logger.warn(
"${project.name}: Using pre-build library $relativePath for targetMachine $variantName."
)
into("$libraryPath/$variantName") {
from(libraryFile)
}
}
targetMachine in taskMap -> {
taskMap[targetMachine]?.let { binary ->
binary.compileTask.get().enabled = true
// Publish optimized binary to reduce size.
binary.linkTask.get().debuggable.set(false)
//Build and copy library
dependsOn(binary.linkTask)
into("$libraryPath/$variantName") {
from(binary.runtimeFile)
}
}
}
else -> {
val downloadUrl =
"https://github.com/weisJ/darklaf/actions?query=workflow%3A%22Build+Native+Libraries%22+is%3Asuccess+branch%3Amaster"
logger.warn(
"""
${project.name}: Library $relativePath for targetMachine $variantName does not exist.
${" ".repeat(project.name.length + 1)} Download it from $downloadUrl
""".trimIndent()
)
}
}
}
}
}
}

21
core/build.gradle.kts

@ -1,6 +1,5 @@
plugins { plugins {
`java-library` `java-library`
id("com.github.johnrengelman.shadow")
id("com.github.vlsi.crlf") id("com.github.vlsi.crlf")
} }
@ -42,27 +41,9 @@ val fontTest by tasks.registering(JavaExec::class) {
classpath(sourceSets.main.get().runtimeClasspath, sourceSets.test.get().runtimeClasspath) classpath(sourceSets.main.get().runtimeClasspath, sourceSets.test.get().runtimeClasspath)
} }
tasks.shadowJar {
exclude("help/")
exclude("icons/")
exclude("org/jdesktop/jxlayer/plaf/ext/images/")
exclude("com/sun/jna/darwin/")
exclude("com/sun/jna/freebsd-x86/")
exclude("com/sun/jna/freebsd-x86-64/")
exclude("com/sun/jna/linux-arm/")
exclude("com/sun/jna/linux-x86/")
exclude("com/sun/jna/linux-x86-64/")
exclude("com/sun/jna/openbsd-x86/")
exclude("com/sun/jna/openbsd-x86-64/")
exclude("com/sun/jna/sunos-sparc/")
exclude("com/sun/jna/sunos-sparcv9/")
exclude("com/sun/jna/sunos-x86/")
exclude("com/sun/jna/sunos-x86-64/")
}
abstract class DemoTask : JavaExec() { abstract class DemoTask : JavaExec() {
init { init {
main = "UIDemo" main = "DemoLauncher"
} }
@Option( @Option(

2
core/src/main/java/com/github/weisj/darklaf/settings/ThemeSettingsPanel.java

@ -556,7 +556,7 @@ public class ThemeSettingsPanel extends JPanel {
themeFollowsSystem = new JCheckBox(resourceBundle.getString("check_system_theme")) { themeFollowsSystem = new JCheckBox(resourceBundle.getString("check_system_theme")) {
@Override @Override
public void setEnabled(final boolean b) { public void setEnabled(final boolean b) {
boolean enabled = b && ThemePreferencesHandler.getSharedInstance().supportsNativeFontSize(); boolean enabled = b && ThemePreferencesHandler.getSharedInstance().supportsNativeTheme();
super.setEnabled(enabled); super.setEnabled(enabled);
} }
}; };

18
core/src/main/java/com/github/weisj/darklaf/ui/scrollpane/DarkMacScrollBarUI.java

@ -25,6 +25,7 @@
package com.github.weisj.darklaf.ui.scrollpane; package com.github.weisj.darklaf.ui.scrollpane;
import java.awt.*; import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import javax.swing.*; import javax.swing.*;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
@ -38,14 +39,23 @@ public class DarkMacScrollBarUI extends DarkScrollBarUI {
return new DarkMacScrollBarUI(); return new DarkMacScrollBarUI();
} }
@Override
protected void paintTrack(final Graphics g, final JComponent c, final Rectangle bounds) {}
@Override @Override
protected void paintMaxiThumb(final Graphics2D g, final Rectangle rect) { protected void paintMaxiThumb(final Graphics2D g, final Rectangle rect) {
GraphicsContext context = GraphicsUtil.setupStrokePainting(g); GraphicsContext context = GraphicsUtil.setupStrokePainting(g);
g.setComposite(COMPOSITE.derive(THUMB_ALPHA)); g.setComposite(COMPOSITE.derive(thumbAlpha));
g.setColor(getThumbColor());
boolean horizontal = scrollbar.getOrientation() == JScrollBar.HORIZONTAL; boolean horizontal = scrollbar.getOrientation() == JScrollBar.HORIZONTAL;
int arc = horizontal ? (rect.height - 2) : (rect.width - 2); int ins = 2;
g.fillRoundRect(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2, arc, arc); int arc = horizontal ? (rect.height - 2 * ins) : (rect.width - 2 * ins);
RoundRectangle2D roundRect = new RoundRectangle2D.Float();
roundRect.setRoundRect(rect.x + ins, rect.y + ins,
rect.width - 2 * ins, rect.height - 2 * ins, arc, arc);
g.setColor(getThumbColor());
g.fill(roundRect);
g.setColor(getThumbBorderColor());
g.draw(roundRect);
context.restore(); context.restore();
} }
} }

19
core/src/main/java/com/github/weisj/darklaf/ui/scrollpane/DarkScrollBarUI.java

@ -39,7 +39,6 @@ import com.github.weisj.darklaf.util.ColorUtil;
*/ */
public class DarkScrollBarUI extends BasicScrollBarUI implements ScrollBarConstants { public class DarkScrollBarUI extends BasicScrollBarUI implements ScrollBarConstants {
protected static final float THUMB_ALPHA = 0.6f;
protected static final AlphaComposite COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER); protected static final AlphaComposite COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
protected DarkScrollBarListener scrollBarListener; protected DarkScrollBarListener scrollBarListener;
protected Color thumbBorderColor; protected Color thumbBorderColor;
@ -48,6 +47,7 @@ public class DarkScrollBarUI extends BasicScrollBarUI implements ScrollBarConsta
protected Color trackBackground; protected Color trackBackground;
protected int smallSize; protected int smallSize;
protected int size; protected int size;
protected float thumbAlpha;
public static ComponentUI createUI(final JComponent c) { public static ComponentUI createUI(final JComponent c) {
return new DarkScrollBarUI(); return new DarkScrollBarUI();
@ -77,6 +77,7 @@ public class DarkScrollBarUI extends BasicScrollBarUI implements ScrollBarConsta
trackBackground = UIManager.getColor("ScrollBar.trackColor"); trackBackground = UIManager.getColor("ScrollBar.trackColor");
smallSize = UIManager.getInt("ScrollBar.smallWidth"); smallSize = UIManager.getInt("ScrollBar.smallWidth");
size = UIManager.getInt("ScrollBar.width"); size = UIManager.getInt("ScrollBar.width");
thumbAlpha = UIManager.getInt("ScrollBar.thumbAlpha") / 100.0f;
} }
@Override @Override
@ -148,14 +149,16 @@ public class DarkScrollBarUI extends BasicScrollBarUI implements ScrollBarConsta
} }
protected void paintMaxiThumb(final Graphics2D g, final Rectangle rect) { protected void paintMaxiThumb(final Graphics2D g, final Rectangle rect) {
g.setComposite(COMPOSITE.derive(THUMB_ALPHA)); g.setComposite(COMPOSITE.derive(thumbAlpha));
Color thumbColor = getThumbColor(); g.setColor(getThumbBorderColor());
float thumbAlpha = scrollBarListener.getThumbAlpha();
double percent = Math.min(1.0, Math.max(0.0, 1 - (thumbAlpha - THUMB_ALPHA)));
g.setColor(ColorUtil.blendColors(thumbBorderColor, thumbColor, percent));
PaintUtil.drawRect(g, rect.x, rect.y, rect.width, rect.height, 1); PaintUtil.drawRect(g, rect.x, rect.y, rect.width, rect.height, 1);
g.setColor(thumbColor); g.setColor(getThumbColor());
g.fillRect(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2); PaintUtil.fillRect(g, rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
}
protected Color getThumbBorderColor() {
double percent = Math.min(1.0, Math.max(0.0, 1 - (scrollBarListener.getThumbAlpha() - thumbAlpha)));
return ColorUtil.blendColors(thumbBorderColor, thumbColor, percent);
} }
protected Color getThumbColor() { protected Color getThumbColor() {

3
core/src/main/resources/com/github/weisj/darklaf/properties/platform/mac.properties

@ -25,6 +25,9 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
# #
ScrollBarUI = com.github.weisj.darklaf.ui.scrollpane.DarkMacScrollBarUI ScrollBarUI = com.github.weisj.darklaf.ui.scrollpane.DarkMacScrollBarUI
ScrollBar.smallWidth = 10
ScrollBar.width = 12
Table.alternateRowColor = true Table.alternateRowColor = true
Tree.alternateRowColor = true Tree.alternateRowColor = true
List.alternateRowColor = true List.alternateRowColor = true

9
core/src/main/resources/com/github/weisj/darklaf/properties/ui/scrollBar.properties

@ -25,12 +25,13 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
# #
ScrollBarUI = com.github.weisj.darklaf.ui.scrollpane.DarkScrollBarUI ScrollBarUI = com.github.weisj.darklaf.ui.scrollpane.DarkScrollBarUI
ScrollBar.fadeStartColor = %controlFadeStart ScrollBar.fadeStartColor = %controlFadeStartSecondary
ScrollBar.fadeEndColor = %controlFadeEnd ScrollBar.fadeEndColor = %controlFadeEndSecondary
ScrollBar.trackColor = %controlTrack ScrollBar.trackColor = %controlTrack
ScrollBar.thumbBorderColor = %controlBorderSecondary ScrollBar.thumbBorderColor = %controlBorderSecondary
ScrollBar.thumb = %controlFillSecondary ScrollBar.thumb = %controlFillSecondary
ScrollBar.thumbShadow = null ScrollBar.thumbShadow = null
ScrollBar.thumbHighlight = null ScrollBar.thumbHighlight = null
ScrollBar.smallWidth = 10 ScrollBar.smallWidth = 8
ScrollBar.width = 12 ScrollBar.width = 10
ScrollBar.thumbAlpha = 60

2
core/src/test/java/ui/ComponentDemo.java

@ -64,6 +64,8 @@ public interface ComponentDemo {
} }
static void showDemo(final ComponentDemo demo, final Dimension dimension, final boolean asDialog) { static void showDemo(final ComponentDemo demo, final Dimension dimension, final boolean asDialog) {
LafManager.enabledPreferenceChangeReporting(false);
System.setProperty("apple.laf.useScreenMenuBar", "true");
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
if (!LafManager.isInstalled()) { if (!LafManager.isInstalled()) {
LafManager.install(demo.createTheme()); LafManager.install(demo.createTheme());

1
core/src/test/java/ui/PreferenceChangeDemo.java

@ -38,6 +38,7 @@ import com.github.weisj.darklaf.theme.Theme;
public class PreferenceChangeDemo implements ComponentDemo { public class PreferenceChangeDemo implements ComponentDemo {
public static void main(final String[] args) { public static void main(final String[] args) {
LafManager.enabledPreferenceChangeReporting(false);
ComponentDemo.showDemo(new PreferenceChangeDemo()); ComponentDemo.showDemo(new PreferenceChangeDemo());
} }

1
gradle.properties

@ -11,7 +11,6 @@ darklaf.version = 2.2.0
# Plugins # Plugins
shadow.version = 5.1.0 shadow.version = 5.1.0
com.github.vlsi.vlsi-release-plugins.version = 1.70 com.github.vlsi.vlsi-release-plugins.version = 1.70
com.github.johnrengelman.shadow.version = 5.1.0
com.github.autostyle.version = 3.1 com.github.autostyle.version = 3.1
# Dependencies # Dependencies

79
macos/build.gradle.kts

@ -1,52 +1,51 @@
import JniUtils.asVariantName
plugins { plugins {
`jni-library` java
id("dev.nokee.jni-library")
id("dev.nokee.objective-cpp-language")
`uber-jni-jar`
`use-prebuilt-binaries`
} }
fun DependencyHandlerScope.javaImplementation(dep: Any) { library {
compileOnly(dep) val minOs = "10.10"
runtimeOnly(dep) val frameworkVersion = "10.15"
}
dependencies { dependencies {
javaImplementation(project(":darklaf-theme")) jvmImplementation(project(":darklaf-theme"))
javaImplementation(project(":darklaf-native-utils")) jvmImplementation(project(":darklaf-native-utils"))
javaImplementation(project(":darklaf-utils")) jvmImplementation(project(":darklaf-utils"))
javaImplementation(project(":darklaf-platform-base")) jvmImplementation(project(":darklaf-platform-base"))
javaImplementation(project(":darklaf-property-loader")) jvmImplementation(project(":darklaf-property-loader"))
nativeImplementation("dev.nokee.framework:JavaVM:[$frameworkVersion,)")
nativeImplementation("dev.nokee.framework:JavaVM:[$frameworkVersion,)") {
capabilities {
requireCapability("JavaVM:JavaNativeFoundation:[$frameworkVersion,)")
} }
}
val macPath by tasks.registering(MacOSSdkPathTask::class) nativeImplementation("dev.nokee.framework:AppKit:[$frameworkVersion,)")
nativeImplementation("dev.nokee.framework:Cocoa:[$frameworkVersion,)")
val sdkRoot: Provider<String> get() = macPath.map { it.sdkPath.absolutePath }
fun ListProperty<String>.addJavaFrameworks() {
addAll("-framework", "JavaNativeFoundation")
add("-F")
add(sdkRoot.map { "$it/System/Library/Frameworks/JavaVM.framework/Frameworks" })
add("-F")
add("/System/Library/Frameworks/JavaVM.framework/Frameworks")
} }
library {
targetMachines.addAll(machines.macOS.x86_64) targetMachines.addAll(machines.macOS.x86_64)
binaries.configureEach { variants.configureEach {
compileTask.get().apply { resourcePath.set("com/github/weisj/darklaf/platform/${project.name}/${asVariantName(targetMachine)}")
dependsOn(macPath) sharedLibrary {
compilerArgs.addAll("-x", "objective-c++") compileTasks.configureEach {
compilerArgs.addAll("-mmacosx-version-min=10.10") compilerArgs.addAll("-mmacosx-version-min=$minOs")
compilerArgs.addAll("-Wunguarded-availability") // Build type not modeled yet, assuming release
compilerArgs.addJavaFrameworks() compilerArgs.addAll(toolChain.map {
source.from( when (it) {
file("src/main/objectiveCpp/Decorations.mm"), is Gcc, is Clang -> listOf("-O2")
file("src/main/objectiveCpp/ThemeInfo.mm") is VisualCpp -> listOf("/O2")
) else -> emptyList()
} }
} })
binaries.whenElementFinalized(CppSharedLibrary::class) { }
linkTask.get().apply { linkTask.configure {
dependsOn(macPath) linkerArgs.addAll("-lobjc", "-mmacosx-version-min=$minOs")
linkerArgs.addAll("-lobjc", "-framework", "AppKit") }
linkerArgs.addJavaFrameworks()
} }
} }
} }

1
macos/gradle.properties

@ -1 +0,0 @@
defaultLibraryName = libdarklaf-macos.dylib

0
macos/libraries/macos-x86-64/library.md → macos/pre-build-libraries/macos-x86-64/library.md

4
macos/src/main/java/com/github/weisj/darklaf/platform/macos/JNIThemeInfoMacOS.java

@ -109,4 +109,8 @@ public class JNIThemeInfoMacOS {
* @param listenerPtr pointer to the listener. * @param listenerPtr pointer to the listener.
*/ */
public static native void deletePreferenceChangeListener(final long listenerPtr); public static native void deletePreferenceChangeListener(final long listenerPtr);
public static native void patchAppBundle(final boolean isJava11OrOlder);
public static native void unpatchAppBundle();
} }

13
macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSLibrary.java

@ -42,19 +42,8 @@ public class MacOSLibrary extends AbstractLibrary {
super(PATH, DLL_NAME, LogUtil.getLogger(MacOSLibrary.class)); super(PATH, DLL_NAME, LogUtil.getLogger(MacOSLibrary.class));
} }
@Override
protected String getLibraryPath() {
if (!SystemInfo.isX64) {
logger.warning("JRE model '"
+ SystemInfo.jreArchitecture
+ "' not supported. Native features will be disabled");
return null;
}
return super.getLibraryPath();
}
@Override @Override
protected boolean canLoad() { protected boolean canLoad() {
return SystemInfo.isMacOSYosemite; return SystemInfo.isX64 && SystemInfo.isMacOSYosemite;
} }
} }

13
macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSThemePreferenceProvider.java

@ -27,6 +27,8 @@ package com.github.weisj.darklaf.platform.macos;
import java.awt.*; import java.awt.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.swing.*;
import com.github.weisj.darklaf.theme.info.*; import com.github.weisj.darklaf.theme.info.*;
import com.github.weisj.darklaf.util.SystemInfo; import com.github.weisj.darklaf.util.SystemInfo;
@ -79,6 +81,17 @@ public class MacOSThemePreferenceProvider implements ThemePreferenceProvider {
@Override @Override
public void initialize() { public void initialize() {
MacOSLibrary.get().updateLibrary(); MacOSLibrary.get().updateLibrary();
if (MacOSLibrary.get().isLoaded()) {
/*
* Patching the app bundle doesn't work anymore in JDK 14. I am not sure what the last version is,
* where it still works, so for now everything below or equal to JDK 11 will benefit from the newer
* catalina algorithm. For everything else we fall back to the Mojave style. This should still give a
* correct answer most of the time.
*/
JNIThemeInfoMacOS.patchAppBundle(!SystemInfo.isJavaVersionAtLeast("12"));
SwingUtilities.invokeLater(() -> { /* Do nothing. This simply forces native resources to be loaded */ });
JNIThemeInfoMacOS.unpatchAppBundle();
}
} }
@Override @Override

0
macos/src/main/objectiveCpp/Decorations.mm → macos/src/main/objcpp/Decorations.mm

66
macos/src/main/objectiveCpp/ThemeInfo.mm → macos/src/main/objcpp/ThemeInfo.mm

@ -27,8 +27,9 @@
#define OBJC(jl) ((id)jlong_to_ptr(jl)) #define OBJC(jl) ((id)jlong_to_ptr(jl))
#define NSRequiresAquaSystemAppearance CFSTR("NSRequiresAquaSystemAppearance")
#define KEY_APPLE_INTERFACE_STYLE @"AppleInterfaceStyle" #define KEY_APPLE_INTERFACE_STYLE @"AppleInterfaceStyle"
#define KEY_SWITCHES_AUTOMATICALLY @"AppleInterfaceStyleSwitchesAutomatically"
#define KEY_ACCENT_COLOR @"AppleAccentColor" #define KEY_ACCENT_COLOR @"AppleAccentColor"
#define KEY_SELECTION_COLOR @"selectedTextBackgroundColor" #define KEY_SELECTION_COLOR @"selectedTextBackgroundColor"
#define KEY_SYSTEM_COLOR_LIST @"System" #define KEY_SYSTEM_COLOR_LIST @"System"
@ -44,6 +45,9 @@
#define VALUE_NO_ACCENT_COLOR (-100) #define VALUE_NO_ACCENT_COLOR (-100)
#define VALUE_NO_SELECTION_COLOR (-1) #define VALUE_NO_SELECTION_COLOR (-1)
BOOL patched = NO;
BOOL catalinaEnabled = NO;
@interface PreferenceChangeListener:NSObject { @interface PreferenceChangeListener:NSObject {
@public JavaVM *jvm; @public JavaVM *jvm;
@public jobject callback; @public jobject callback;
@ -101,19 +105,35 @@
} }
- (void)notificationEvent:(NSNotification *)notification { - (void)notificationEvent:(NSNotification *)notification {
[JNFRunLoop performOnMainThreadWaiting:NO withBlock:^{
[self runCallback]; [self runCallback];
}];
} }
@end @end
BOOL isDarkModeCatalina() {
NSAppearance *appearance = NSApp.effectiveAppearance;
NSAppearanceName appearanceName = [appearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua,
NSAppearanceNameDarkAqua]];
return [appearanceName isEqualToString:NSAppearanceNameDarkAqua];
}
BOOL isDarkModeMojave() {
NSString *interfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:KEY_APPLE_INTERFACE_STYLE];
return [VALUE_DARK caseInsensitiveCompare:interfaceStyle] == NSOrderedSame;
}
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_github_weisj_darklaf_platform_macos_JNIThemeInfoMacOS_isDarkThemeEnabled(JNIEnv *env, jclass obj) { Java_com_github_weisj_darklaf_platform_macos_JNIThemeInfoMacOS_isDarkThemeEnabled(JNIEnv *env, jclass obj) {
JNF_COCOA_ENTER(env); JNF_COCOA_ENTER(env);
if(@available(macOS 10.15, *)) {
if (catalinaEnabled) {
return (jboolean) isDarkModeCatalina();
}
}
if (@available(macOS 10.14, *)) { if (@available(macOS 10.14, *)) {
NSString *interfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:KEY_APPLE_INTERFACE_STYLE]; return (jboolean) isDarkModeMojave();
// interfaceStyle can be nil (light mode) or "Dark" (dark mode).
BOOL isDark = [VALUE_DARK caseInsensitiveCompare:interfaceStyle] == NSOrderedSame;
return (jboolean) isDark;
} else { } else {
return (jboolean) NO; return (jboolean) NO;
} }
@ -182,3 +202,39 @@ JNF_COCOA_ENTER(env);
} }
JNF_COCOA_EXIT(env); JNF_COCOA_EXIT(env);
} }
JNIEXPORT void JNICALL
Java_com_github_weisj_darklaf_platform_macos_JNIThemeInfoMacOS_patchAppBundle(JNIEnv *env, jclass obj, jboolean preJava11) {
JNF_COCOA_ENTER(env);
if (@available(macOS 10.15, *)) {
NSString *name = [[NSBundle mainBundle] bundleIdentifier];
CFStringRef bundleName = (__bridge CFStringRef)name;
Boolean exists = false;
CFPreferencesGetAppBooleanValue(NSRequiresAquaSystemAppearance, bundleName, &exists);
catalinaEnabled = preJava11 || exists;
if (!exists) {
// Only patch if value hasn't been explicitly set
CFPreferencesSetAppValue(NSRequiresAquaSystemAppearance, kCFBooleanFalse, bundleName);
CFPreferencesAppSynchronize(bundleName);
patched = YES;
}
}
JNF_COCOA_EXIT(env);
}
JNIEXPORT void JNICALL
Java_com_github_weisj_darklaf_platform_macos_JNIThemeInfoMacOS_unpatchAppBundle(JNIEnv *env, jclass obj) {
JNF_COCOA_ENTER(env);
if (!patched) return;
if (@available(macOS 10.15, *)) {
NSString *name = [[NSBundle mainBundle] bundleIdentifier];
CFStringRef bundleName = (__bridge CFStringRef)name;
CFPreferencesSetAppValue(NSRequiresAquaSystemAppearance, nil, bundleName);
CFPreferencesAppSynchronize(bundleName);
}
JNF_COCOA_EXIT(env);
}

20
native-utils/src/main/java/com/github/weisj/darklaf/platform/AbstractLibrary.java

@ -63,14 +63,12 @@ public abstract class AbstractLibrary {
String path = getLibraryPath(); String path = getLibraryPath();
if (path != null && !path.isEmpty()) { if (path != null && !path.isEmpty()) {
NativeUtil.loadLibraryFromJar(getLibraryPath()); NativeUtil.loadLibraryFromJar(getLibraryPath());
}
loaded = true; loaded = true;
logger.info("Loaded " + getLibraryName() + ". Native features are enabled."); info("Loaded " + getLibraryName() + ".");
}
} catch (Throwable e) { } catch (Throwable e) {
// Library not found, SecurityManager prevents library loading etc. // Library not found, SecurityManager prevents library loading etc.
logger.log(Level.SEVERE, error("Could not load library " + getLibraryName() + ".", e);
"Could not load library " + getLibraryName() + ". Native features will be disabled",
e);
} }
} }
@ -91,4 +89,16 @@ public abstract class AbstractLibrary {
public boolean isLoaded() { public boolean isLoaded() {
return loaded; return loaded;
} }
protected void info(final String message) {
if (logger != null) logger.info(message);
}
protected void warning(final String message) {
if (logger != null) logger.warning(message);
}
protected void error(final String message, final Throwable e) {
if (logger != null) logger.log(Level.SEVERE, message, e);
}
} }

1
settings.gradle.kts

@ -4,7 +4,6 @@ pluginManagement {
fun PluginDependenciesSpec.idv(id: String, key: String = id) = id(id) version key.v() fun PluginDependenciesSpec.idv(id: String, key: String = id) = id(id) version key.v()
idv("com.github.autostyle") idv("com.github.autostyle")
idv("com.github.johnrengelman.shadow")
idv("com.github.vlsi.crlf", "com.github.vlsi.vlsi-release-plugins") idv("com.github.vlsi.crlf", "com.github.vlsi.vlsi-release-plugins")
idv("com.github.vlsi.gradle-extensions", "com.github.vlsi.vlsi-release-plugins") idv("com.github.vlsi.gradle-extensions", "com.github.vlsi.vlsi-release-plugins")
idv("com.github.vlsi.license-gather", "com.github.vlsi.vlsi-release-plugins") idv("com.github.vlsi.license-gather", "com.github.vlsi.vlsi-release-plugins")

2
theme/src/main/resources/com/github/weisj/darklaf/theme/darcula/darcula_defaults.properties

@ -102,6 +102,8 @@ Theme.highContrast = false
%controlBackground = 555555 %controlBackground = 555555
%controlFadeStart = 696969 %controlFadeStart = 696969
%controlFadeEnd = 838383 %controlFadeEnd = 838383
%controlFadeStartSecondary = 696969
%controlFadeEndSecondary = 8f8f8f
%controlErrorFadeStart = e74848 %controlErrorFadeStart = e74848
%controlErrorFadeEnd = f4a2a0 %controlErrorFadeEnd = f4a2a0
%controlPassedFadeStart = 008f50 %controlPassedFadeStart = 008f50

2
theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_dark/high_contrast_dark_defaults.properties

@ -102,6 +102,8 @@ Theme.highContrast = true
%controlBackground = 404040 %controlBackground = 404040
%controlFadeStart = FFFFFF %controlFadeStart = FFFFFF
%controlFadeEnd = 1A1A1A %controlFadeEnd = 1A1A1A
%controlFadeStartSecondary = B2B2B2
%controlFadeEndSecondary = FFFFFF
%controlErrorFadeStart = E6194B %controlErrorFadeStart = E6194B
%controlErrorFadeEnd = 431C27 %controlErrorFadeEnd = 431C27
%controlPassedFadeStart = 00E61F %controlPassedFadeStart = 00E61F

2
theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_dark/high_contrast_dark_ui.properties

@ -28,5 +28,5 @@ ColorChooser.swatchesDefaultRecentColor = 000000
Button.borderless.drawOutline = true Button.borderless.drawOutline = true
TabbedPane.selectedHoverBackground = %backgroundHover TabbedPane.selectedHoverBackground = %backgroundHover
ToolTip.paintShadow = false ToolTip.paintShadow = false
ScrollBar.fadeEndColor = %ScrollBar.fadeStartColor
RootPane.borderInsets = 1,1,1,1 RootPane.borderInsets = 1,1,1,1
ScrollBar.thumbAlpha = 100

2
theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_light/high_contrast_light_defaults.properties

@ -102,6 +102,8 @@ Theme.highContrast = true
%controlBackground = BFBFBF %controlBackground = BFBFBF
%controlFadeStart = 000000 %controlFadeStart = 000000
%controlFadeEnd = E5E5E5 %controlFadeEnd = E5E5E5
%controlFadeStartSecondary = 6B6B6B
%controlFadeEndSecondary = 0B0B0B
%controlErrorFadeStart = E6194B %controlErrorFadeStart = E6194B
%controlErrorFadeEnd = E3BCC7 %controlErrorFadeEnd = E3BCC7
%controlPassedFadeStart = 19FF38 %controlPassedFadeStart = 19FF38

2
theme/src/main/resources/com/github/weisj/darklaf/theme/high_contrast_light/high_contrast_light_ui.properties

@ -31,5 +31,5 @@ TableHeader.foreground = FFFFFF
TabbedPane.selectedHoverBackground = %backgroundHover TabbedPane.selectedHoverBackground = %backgroundHover
TabFrameTab.selectedForeground = %textSelectionForeground TabFrameTab.selectedForeground = %textSelectionForeground
TabFrameTab.hoverForeground = %textSelectionForeground TabFrameTab.hoverForeground = %textSelectionForeground
ScrollBar.fadeEndColor = %ScrollBar.fadeStartColor
RootPane.borderInsets = 1,1,1,1 RootPane.borderInsets = 1,1,1,1
ScrollBar.thumbAlpha = 100

2
theme/src/main/resources/com/github/weisj/darklaf/theme/intellij/intellij_defaults.properties

@ -122,6 +122,8 @@ Theme.highContrast = false
%controlBackground = C4C4C4 %controlBackground = C4C4C4
%controlFadeStart = C4C4C4 %controlFadeStart = C4C4C4
%controlFadeEnd = 808080 %controlFadeEnd = 808080
%controlFadeStartSecondary = 808080
%controlFadeEndSecondary = 4c4c4c
%controlErrorFadeStart = d64f4f %controlErrorFadeStart = d64f4f
%controlErrorFadeEnd = fb8f89 %controlErrorFadeEnd = fb8f89
%controlPassedFadeStart = 34b171 %controlPassedFadeStart = 34b171

2
theme/src/main/resources/com/github/weisj/darklaf/theme/one_dark/one_dark_defaults.properties

@ -102,6 +102,8 @@ Theme.highContrast = false
%controlBackground = 1D1D26 %controlBackground = 1D1D26
%controlFadeStart = 568AF2 %controlFadeStart = 568AF2
%controlFadeEnd = 313469 %controlFadeEnd = 313469
%controlFadeStartSecondary = 676A71
%controlFadeEndSecondary = 868A91
%controlErrorFadeStart = bd3c5f %controlErrorFadeStart = bd3c5f
%controlErrorFadeEnd = 472c33 %controlErrorFadeEnd = 472c33
%controlPassedFadeStart = 239E62 %controlPassedFadeStart = 239E62

2
theme/src/main/resources/com/github/weisj/darklaf/theme/one_dark/one_dark_ui.properties

@ -28,8 +28,6 @@ CheckBox.activeFillColor = %textBackground
CheckBox.selectedFillColor = %textBackground CheckBox.selectedFillColor = %textBackground
Slider.activeThumbFill = %textBackground Slider.activeThumbFill = %textBackground
RadioButton.activeFillColor = %textBackground RadioButton.activeFillColor = %textBackground
ScrollBar.fadeStartColor = 676A71
ScrollBar.fadeEndColor = 797E85
Menu.selectionBackground = %backgroundColorfulInactive Menu.selectionBackground = %backgroundColorfulInactive
MenuItem.selectionBackground = %backgroundColorfulInactive MenuItem.selectionBackground = %backgroundColorfulInactive
RadioButtonMenuItem.selectionBackground = %backgroundColorfulInactive RadioButtonMenuItem.selectionBackground = %backgroundColorfulInactive

2
theme/src/main/resources/com/github/weisj/darklaf/theme/solarized_dark/solarized_dark_defaults.properties

@ -102,6 +102,8 @@ Theme.highContrast = false
%controlBackground = 2F535F %controlBackground = 2F535F
%controlFadeStart = 476971 %controlFadeStart = 476971
%controlFadeEnd = 67848C %controlFadeEnd = 67848C
%controlFadeStartSecondary = 476971
%controlFadeEndSecondary = 769097
%controlErrorFadeStart = DE4647 %controlErrorFadeStart = DE4647
%controlErrorFadeEnd = EFA0A0 %controlErrorFadeEnd = EFA0A0
%controlPassedFadeStart = 008A4B %controlPassedFadeStart = 008A4B

2
theme/src/main/resources/com/github/weisj/darklaf/theme/solarized_light/solarized_light_defaults.properties

@ -102,6 +102,8 @@ Theme.highContrast = false
%controlBackground = C3BEAF %controlBackground = C3BEAF
%controlFadeStart = C3BEAF %controlFadeStart = C3BEAF
%controlFadeEnd = 6B8086 %controlFadeEnd = 6B8086
%controlFadeStartSecondary = AFAB9D
%controlFadeEndSecondary = 6B8086
%controlErrorFadeStart = D14943 %controlErrorFadeStart = D14943
%controlErrorFadeEnd = F6887C %controlErrorFadeEnd = F6887C
%controlPassedFadeStart = 2DAA66 %controlPassedFadeStart = 2DAA66

57
windows/build.gradle.kts

@ -1,41 +1,54 @@
import JniUtils.asVariantName
plugins { plugins {
`jni-library` java
} id("dev.nokee.jni-library")
id("dev.nokee.cpp-language")
fun DependencyHandlerScope.javaImplementation(dep: Any) { `uber-jni-jar`
compileOnly(dep) `use-prebuilt-binaries`
runtimeOnly(dep)
} }
library {
dependencies { dependencies {
javaImplementation(project(":darklaf-native-utils")) jvmImplementation(project(":darklaf-native-utils"))
javaImplementation(project(":darklaf-utils")) jvmImplementation(project(":darklaf-utils"))
javaImplementation(project(":darklaf-platform-base")) jvmImplementation(project(":darklaf-platform-base"))
javaImplementation(project(":darklaf-theme")) jvmImplementation(project(":darklaf-theme"))
javaImplementation(project(":darklaf-property-loader")) jvmImplementation(project(":darklaf-property-loader"))
javaImplementation("net.java.dev.jna:jna") jvmImplementation("net.java.dev.jna:jna")
} }
library {
targetMachines.addAll(machines.windows.x86, machines.windows.x86_64) targetMachines.addAll(machines.windows.x86, machines.windows.x86_64)
binaries.configureEach { variants.configureEach {
compileTask.get().compilerArgs.addAll( resourcePath.set("com/github/weisj/darklaf/platform/${project.name}/${asVariantName(targetMachine)}")
when (toolChain) { sharedLibrary {
compileTasks.configureEach {
compilerArgs.addAll(toolChain.map {
when (it) {
is Gcc, is Clang -> listOf("--std=c++11") is Gcc, is Clang -> listOf("--std=c++11")
is VisualCpp -> listOf("/EHsc") is VisualCpp -> listOf("/EHsc")
else -> emptyList() else -> emptyList()
} }
) })
// Build type not modeled yet, assuming release
compilerArgs.addAll(toolChain.map {
when (it) {
is Gcc, is Clang -> listOf("-O2")
is VisualCpp -> listOf("/O2")
else -> emptyList()
}
})
} }
binaries.whenElementFinalized(CppSharedLibrary::class) { linkTask.configure {
linkTask.get().linkerArgs.addAll( linkerArgs.addAll(toolChain.map {
when (toolChain) { when (it) {
is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32", "-Shell32") is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32", "-Shell32")
is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib", "Shell32.lib") is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib", "Shell32.lib")
else -> emptyList() else -> emptyList()
} }
) })
}
}
} }
} }

0
windows/libraries/windows-x86-64/library.md → windows/pre-build-libraries/windows-x86-64/library.md

0
windows/libraries/windows-x86/library.md → windows/pre-build-libraries/windows-x86/library.md

13
windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsLibrary.java

@ -44,17 +44,6 @@ public class WindowsLibrary extends AbstractLibrary {
super(PATH, DLL_NAME, LogUtil.getLogger(WindowsLibrary.class)); super(PATH, DLL_NAME, LogUtil.getLogger(WindowsLibrary.class));
} }
@Override
protected String getLibraryPath() {
if (SystemInfo.undefinedArchitecture) {
logger.warning("Could not determine jre model '"
+ SystemInfo.jreArchitecture
+ "'. Native features will be disabled");
return null;
}
return super.getLibraryPath();
}
@Override @Override
protected String getPath() { protected String getPath() {
if (SystemInfo.isX86) { if (SystemInfo.isX86) {
@ -68,6 +57,6 @@ public class WindowsLibrary extends AbstractLibrary {
@Override @Override
protected boolean canLoad() { protected boolean canLoad() {
return SystemInfo.isWindows10; return !SystemInfo.undefinedArchitecture && SystemInfo.isWindows10;
} }
} }

Loading…
Cancel
Save