mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00:00
Merge branch mac-window into master
This commit is contained in:
parent
0e62af8817
commit
64f58d37a4
8 changed files with 149 additions and 13 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -19,3 +19,6 @@ ComponentsGenerated.wxs
|
||||||
!dist/javafx/**/lib
|
!dist/javafx/**/lib
|
||||||
!dist/javafx/**/bin
|
!dist/javafx/**/bin
|
||||||
dev.properties
|
dev.properties
|
||||||
|
xcuserdata/
|
||||||
|
*.dylib
|
||||||
|
project.xcworkspace
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class AppTheme {
|
||||||
|
|
||||||
public static void initThemeHandlers(Stage stage) {
|
public static void initThemeHandlers(Stage stage) {
|
||||||
Runnable r = () -> {
|
Runnable r = () -> {
|
||||||
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass(OsType.getLocal().getId()), true);
|
||||||
if (AppPrefs.get() == null) {
|
if (AppPrefs.get() == null) {
|
||||||
var def = Theme.getDefaultLightTheme();
|
var def = Theme.getDefaultLightTheme();
|
||||||
stage.getScene().getRoot().getStyleClass().add(def.getCssId());
|
stage.getScene().getRoot().getStyleClass().add(def.getCssId());
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.apache.commons.lang3.SystemUtils;
|
||||||
public class ModifiedStage extends Stage {
|
public class ModifiedStage extends Stage {
|
||||||
|
|
||||||
public static boolean mergeFrame() {
|
public static boolean mergeFrame() {
|
||||||
return SystemUtils.IS_OS_WINDOWS_11;
|
return SystemUtils.IS_OS_WINDOWS_11 || SystemUtils.IS_OS_MAC;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
|
@ -55,12 +55,23 @@ public class ModifiedStage extends Stage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OsType.getLocal() != OsType.WINDOWS || AppPrefs.get() == null || AppPrefs.get().theme.getValue() == null) {
|
if (OsType.getLocal() == OsType.LINUX || AppPrefs.get() == null || AppPrefs.get().theme.getValue() == null) {
|
||||||
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), false);
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), false);
|
||||||
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), true);
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (OsType.getLocal()) {
|
||||||
|
case OsType.Linux linux -> {
|
||||||
|
}
|
||||||
|
case OsType.MacOs macOs -> {
|
||||||
|
var ctrl = new NativeMacOsWindowControl(stage);
|
||||||
|
var seamlessFrame = !AppPrefs.get().performanceMode().get() && mergeFrame();
|
||||||
|
var seamlessFrameApplied = seamlessFrame && ctrl.setAppearance(seamlessFrame, AppPrefs.get().theme.getValue().isDark());
|
||||||
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrameApplied);
|
||||||
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrameApplied);
|
||||||
|
}
|
||||||
|
case OsType.Windows windows -> {
|
||||||
var ctrl = new NativeWinWindowControl(stage);
|
var ctrl = new NativeWinWindowControl(stage);
|
||||||
ctrl.setWindowAttribute(
|
ctrl.setWindowAttribute(
|
||||||
NativeWinWindowControl.DmwaWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE.get(),
|
NativeWinWindowControl.DmwaWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE.get(),
|
||||||
|
@ -74,6 +85,8 @@ public class ModifiedStage extends Stage {
|
||||||
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrame);
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrame);
|
||||||
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrame);
|
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrame);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void updateStage(Stage stage) {
|
private static void updateStage(Stage stage) {
|
||||||
if (!stage.isShowing()) {
|
if (!stage.isShowing()) {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package io.xpipe.app.core.window;
|
||||||
|
|
||||||
|
import com.sun.jna.NativeLong;
|
||||||
|
import io.xpipe.app.util.NativeBridge;
|
||||||
|
import io.xpipe.core.util.ModuleHelper;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class NativeMacOsWindowControl {
|
||||||
|
|
||||||
|
private final long nsWindow;
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public NativeMacOsWindowControl(Window stage) {
|
||||||
|
Method tkStageGetter = Window.class.getDeclaredMethod("getPeer");
|
||||||
|
tkStageGetter.setAccessible(true);
|
||||||
|
Object tkStage = tkStageGetter.invoke(stage);
|
||||||
|
Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow");
|
||||||
|
getPlatformWindow.setAccessible(true);
|
||||||
|
Object platformWindow = getPlatformWindow.invoke(tkStage);
|
||||||
|
Method getNativeHandle = platformWindow.getClass().getMethod("getNativeHandle");
|
||||||
|
getNativeHandle.setAccessible(true);
|
||||||
|
Object nativeHandle = getNativeHandle.invoke(platformWindow);
|
||||||
|
this.nsWindow = (long) nativeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setAppearance(boolean seamlessFrame, boolean darkMode) {
|
||||||
|
if (!ModuleHelper.isImage()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lib = NativeBridge.getMacOsLibrary();
|
||||||
|
if (lib.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lib.get().setAppearance(new NativeLong(nsWindow), seamlessFrame, darkMode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
36
app/src/main/java/io/xpipe/app/util/NativeBridge.java
Normal file
36
app/src/main/java/io/xpipe/app/util/NativeBridge.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
|
import com.sun.jna.Library;
|
||||||
|
import com.sun.jna.Native;
|
||||||
|
import com.sun.jna.NativeLong;
|
||||||
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class NativeBridge {
|
||||||
|
|
||||||
|
private static MacOsLibrary macOsLibrary;
|
||||||
|
private static boolean loadingFailed;
|
||||||
|
|
||||||
|
public static Optional<MacOsLibrary> getMacOsLibrary() {
|
||||||
|
if (macOsLibrary == null && !loadingFailed) {
|
||||||
|
try {
|
||||||
|
System.setProperty("jna.library.path", XPipeInstallation.getCurrentInstallationBasePath()
|
||||||
|
.resolve("Contents").resolve("runtime").resolve("Contents").resolve("Home").resolve("lib").toString());
|
||||||
|
var l = Native.load("xpipe_bridge", MacOsLibrary.class, Map.of());
|
||||||
|
macOsLibrary = l;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
ErrorEvent.fromThrowable(t).handle();
|
||||||
|
loadingFailed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(macOsLibrary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface MacOsLibrary extends Library {
|
||||||
|
|
||||||
|
public abstract void setAppearance(NativeLong window, boolean seamlessFrame, boolean dark);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,10 @@
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root:macos:seamless-frame {
|
||||||
|
-fx-padding: 0 0 25 0;
|
||||||
|
}
|
||||||
|
|
||||||
.root:dark:separate-frame .background {
|
.root:dark:separate-frame .background {
|
||||||
-fx-background-color: derive(-color-bg-default, 1%);
|
-fx-background-color: derive(-color-bg-default, 1%);
|
||||||
}
|
}
|
||||||
|
@ -53,6 +57,11 @@
|
||||||
-fx-padding: 0 0 0 0;
|
-fx-padding: 0 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root:macos:seamless-frame.layout > .background {
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-border-insets: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.root:seamless-frame.layout > .background > * {
|
.root:seamless-frame.layout > .background > * {
|
||||||
-fx-background-radius: 0 10 0 0;
|
-fx-background-radius: 0 10 0 0;
|
||||||
-fx-border-radius: 0 10 0 0;
|
-fx-border-radius: 0 10 0 0;
|
||||||
|
|
|
@ -46,6 +46,8 @@ public interface OsType {
|
||||||
|
|
||||||
sealed interface Local extends OsType permits OsType.Windows, OsType.Linux, OsType.MacOs {
|
sealed interface Local extends OsType permits OsType.Windows, OsType.Linux, OsType.MacOs {
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
|
||||||
default Any toAny() {
|
default Any toAny() {
|
||||||
return (Any) this;
|
return (Any) this;
|
||||||
}
|
}
|
||||||
|
@ -131,6 +133,11 @@ public interface OsType {
|
||||||
return "Windows";
|
return "Windows";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "windows";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Unix implements OsType {
|
class Unix implements OsType {
|
||||||
|
@ -197,6 +204,11 @@ public interface OsType {
|
||||||
|
|
||||||
final class Linux extends Unix implements OsType, Local, Any {
|
final class Linux extends Unix implements OsType, Local, Any {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "linux";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
||||||
try (CommandControl c = pc.command("lsb_release -a").start()) {
|
try (CommandControl c = pc.command("lsb_release -a").start()) {
|
||||||
|
@ -223,6 +235,11 @@ public interface OsType {
|
||||||
|
|
||||||
final class MacOs implements OsType, Local, Any {
|
final class MacOs implements OsType, Local, Any {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "macos";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String makeFileSystemCompatible(String name) {
|
public String makeFileSystemCompatible(String name) {
|
||||||
// Technically the backslash is supported, but it causes all kinds of troubles, so we also exclude it
|
// Technically the backslash is supported, but it causes all kinds of troubles, so we also exclude it
|
||||||
|
|
13
dist/base.gradle
vendored
13
dist/base.gradle
vendored
|
@ -229,6 +229,19 @@ if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
|
||||||
commandLine "$projectDir/misc/mac/sign_and_notarize.sh", "$projectDir", rootProject.arch.toString(), rootProject.productName
|
commandLine "$projectDir/misc/mac/sign_and_notarize.sh", "$projectDir", rootProject.arch.toString(), rootProject.productName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fullVersion) {
|
||||||
|
def nativeLib = "$projectDir/native_lib/macos"
|
||||||
|
def proj = "$nativeLib/xpipe_bridge.xcodeproj"
|
||||||
|
exec {
|
||||||
|
environment 'CONFIGURATION_BUILD_DIR', project.getLayout().getBuildDirectory().dir("native_lib")
|
||||||
|
commandLine 'xcodebuild', '-configuration', 'Release', '-project', proj, '-scheme', 'xpipe_bridge', '-derivedDataPath', project.getLayout().getBuildDirectory().dir("native_lib").get(), 'build'
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from project.getLayout().getBuildDirectory().dir("native_lib/Build/Products/Release").get().file('libxpipe_bridge.dylib')
|
||||||
|
into "$distDir/$app/Contents/runtime/Contents/Home/lib/"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue