有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java macOS“Big-Sur”检测深色菜单栏/系统托盘

从macOS(10.16“Beta”/11.0)“Big Sur”开始,菜单栏和系统托盘不再支持桌面暗模式首选项,因此很难为该桌面正确设置系统托盘图标的主题

以前,使用shell命令default read,可以检测到暗模式:

defaults read -g AppleInterfaceStyle
# "Dark"

这对于检测窗口主题仍然很有效,但对于菜单栏和系统托盘主题不起作用

由于该区域似乎受壁纸亮度/白度/亮度的影响,我们如何检测黑暗的系统托盘

n

如何在(例如)Objective-C/C++中检测此问题?任何解决方案都是受欢迎的,因为大多数方案都可以调整

问题也发布到苹果开发者论坛:https://developer.apple.com/forums/thread/652540

Qt5。6有一个名为^{}的特性,它允许操作系统自动处理这个问题。这实际上是^{}的别名

更多关于macOS“暗模式”的参考:

采用OpenJDK上游错误报告:

关键词:NSStatusBarMenu Bar Extras


共 (4) 个答案

  1. # 1 楼答案

    我也有同样的问题,但我想我找到了解决办法。正如在AppKit Release Notes for Big Sur中所写的(参见NSStatusItem的条目),您只需观察NSStatusItembuttoneffectiveAppearance。如果effectiveAppearance的名称包含dark,则为暗模式。否则就是灯光模式

    我创建的一个示例代码将lightdark显示为NSStatusItem的文本标签,可以在this GitHub repo处找到,具体请参见AppDelegate.m。(我很抱歉因为使用Objective-C而成为濒临灭绝的恐龙。)您可以通过在Catalina或Big-Sur上运行,从系统首选项更改暗/光设置或桌面图片的颜色来测试它

    编辑:事实证明,Big-Sur有时会从亮变亮或从暗变暗(从这个意义上说,尽管外观实际上没有改变,但调用了KVO)因此,建议在更改前后检查effectiveApparance的值,以确认实际更改的值

  2. # 2 楼答案

    我刚刚提交了一份TSI,我得到了一个答案:

    But I would not add an NSView on top of the NSStatusItem’s button directly. The docs mention to use the button property to customize the appearance and behavior of the status item, but it should work within the confines of the button itself, that is, it’s various properties like the image and text and their placements. Years ago, NSStatusItem allowed for custom views, but then became deprecated, in order to support a button-based UI, therefore allowing its drawing behavior to easily adapt to changes in the menu bar’s appearance.

    不幸的是,没有办法通过编程获得这些信息。 然而,获取信息对于我的三个应用程序来说非常重要,所以我开始探索

    对我个人来说,获取这些信息不会触发任何安全提示是非常重要的

    我想到了以下想法:

    • 创建并隐藏NSStatusItem
    • 设置模板化的NSImage
    • CALayer的内容呈现为NSImage

    您可以使用此代码获取颜色信息(注意:NSStatusItem永远不可见,并且不会导致现有项目移动或类似情况)。请随意调整格式和类:

    我用公共属性创建了一个名为MenuBar的类:

    public class MenuBar {
        private static var statusItem: NSStatusItem?
    
        public static var theme: MenuBarTheme {
            if self.statusItem == nil {
                self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
                self.statusItem?.button?.image = NSImage(systemSymbolName: "circle.fill", accessibilityDescription: nil)
                self.statusItem?.isVisible = false
            }
            
            if let color = self.getPixelColor() {
                return color.redComponent < 0.20 && color.blueComponent < 0.20 && color.greenComponent < 0.20 ? .light : .dark
            }
            else
            {
                return NSApplication.isDarkMode ? .dark : .light
            }
        }
    
        public static var tintColor: NSColor {
            return self.theme == .light ? NSColor.black : NSColor.white
      }
        
      // MARK: - Helper
      fileprivate static func getPixelColor() -> NSColor?
      {
         if let image = self.statusItem?.button?.layer?.getBitmapImage() {
            let imageRep = NSBitmapImageRep(data: image.tiffRepresentation!)
                
             if let color = imageRep?.colorAt(x: Int(image.size.width / 2.0), y: Int(image.size.height / 2.0)) {
                return color
             }
         }
            
         return nil
      }
    }
    
    public enum MenuBarTheme : String
    {
        case light = "light"
        case dark = "dark"
    }
    
    public extension NSApplication
    {
        class var isDarkMode: Bool
        {
            return NSApplication.shared.appearance?.description.lowercased().contains("dark") ?? false
        }
    }
    
    public extension CALayer
    {
        func getBitmapImage() -> NSImage
        {
            let btmpImgRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(self.frame.width), pixelsHigh: Int(self.frame.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 32)
    
            let ctx = NSGraphicsContext(bitmapImageRep: btmpImgRep!)
            let cgContext = ctx!.cgContext
            
            self.render(in: cgContext)
            
            let cgImage = cgContext.makeImage()
    
            return NSImage(cgImage: cgImage!, size: CGSize(width: self.frame.width, height: self.frame.height))
        }
    }
    
  3. # 3 楼答案

    对于Java17,添加了此功能。它最终将被后移植到Java11

    https://github.com/openjdk/jdk/pull/481

    新物业为:

    apple.awt.enableTemplateImages
    

    从命令行:

    • -Dapple.awt.enableTemplateImages=true

    。。。或通过Java:

    • System.setProperty("apple.awt.enableTemplateImages", "true");

    请注意,从大Sur开始,图标不再是纯黑色或纯白色的(而是稍微有一点阴影),因此使用模板API对于正确的外观很重要;感觉

  4. # 4 楼答案

    尽管较新的JDK版本will have this ^{} feature natively,但对于较旧的Java版本,此技术使用JNA,并且依赖于https://github.com/dyorgio/macos-tray-icon-fixer项目。在内部,它使用Java反射和JNA指针的组合来更新图像并设置^{} flag to true

    它已经用JDK8和JDK11进行了测试。如果使用带有aarch64 support的JNA端口,它也可以与新的Apple Silicon Mac一起工作

    多亏了@dyorgio的把戏

    // Verify theme to choose image version
    Image iconImage = MacOSTrayIconFixer.getInitialIcon(blackImage, whiteImage);
    // Create your TrayIcon using image
    TrayIcon icon = new TrayIcon(iconImage, "Test");
    // (Optional) add action and menu
    icon.addActionListener((e) -> System.out.println("examples.BasicUsage.main():" + SwingUtilities.isEventDispatchThread()));
    
    // Include in SystemTray BEFORE call fixer
    SystemTray.getSystemTray().add(icon);
    
    // Fix
    MacOSTrayIconFixer.fix(icon, blackImage, whiteImage, false, 0);