It is currently Sat Jul 20, 2019 9:17 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: Detect change between Light and Dark Mode?
PostPosted: Thu Apr 11, 2019 4:23 am 
Offline
Enthusiast
Enthusiast

Joined: Thu Dec 29, 2011 5:03 pm
Posts: 434
Hi,

I want to update my canvas-based gadgets when the user changes their macOS Appearance mode, but in the following, the colours are the same as before when the change happens. What am I doing wrong?
Code:
EnableExplicit

Global text_color, control_background_color

Declare UpdateColors()

Define app = CocoaMessage(0,0,"NSApplication sharedApplication") ; By deseven: https://www.purebasic.fr/english/viewtopic.php?p=494505#p494505
Define appDelegate = CocoaMessage(0,app,"delegate")
Define delegateClass = object_getClass_(appDelegate)
Define selector = sel_registerName_("darkModeChanged:")
Define distributedNotificationCenter = CocoaMessage(0,0,"NSDistributedNotificationCenter defaultCenter")
Procedure darkModeChanged(notification)
  UpdateColors()
EndProcedure

class_addMethod_(delegateClass,selector,@darkModeChanged(),"v@:@")
CocoaMessage(0,distributedNotificationCenter,
             "addObserver:",appDelegate,
             "selector:",selector,
             "name:$",@"AppleInterfaceThemeChangedNotification",
             "object:",#nil)

Procedure.i GetCocoaColor(ColorName.s) ; By wilbert: https://www.purebasic.fr/english/viewtopic.php?p=419571#p419571
  Protected.i Result, Rect.NSRect, Image, NSColor = CocoaMessage(#Null, #Null, "NSColor " + ColorName)
  If NSColor
    Rect\size\width = 1
    Rect\size\height = 1
    Image = CreateImage(#PB_Any, 1, 1)
    StartDrawing(ImageOutput(Image))
    CocoaMessage(#Null, NSColor, "drawSwatchInRect:@", @Rect)
    Result = Point(0, 0)
    StopDrawing()
    FreeImage(Image)
    ProcedureReturn Result
  Else
    ProcedureReturn -1
  EndIf
EndProcedure

; ---

Procedure UpdateColors()
  text_color = GetCocoaColor("textColor")
  control_background_color = GetCocoaColor("controlBackgroundColor")
  If StartDrawing(CanvasOutput(0))
    Box(0, 0, OutputWidth(), OutputHeight(), control_background_color)
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawText(10, 10, "Hello World", text_color)
    StopDrawing()
  EndIf
EndProcedure

OpenWindow(0, #PB_Ignore, #PB_Ignore, 300, 150, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

CanvasGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)

UpdateColors()

Repeat : Until WaitWindowEvent(100) = #PB_Event_CloseWindow


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Fri Apr 12, 2019 5:52 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3351
Location: Netherlands
Try if this works ...
It's based on Key-Value observing

Code:
; >> Key-Value observer code <<<

EnumerationBinary
  #NSKeyValueObservingOptionNew
  #NSKeyValueObservingOptionOld
EndEnumeration

Global *NSKeyValueChangeNewKey.Integer = dlsym_(#RTLD_DEFAULT, "NSKeyValueChangeNewKey")
Global *NSKeyValueChangeOldKey.Integer = dlsym_(#RTLD_DEFAULT, "NSKeyValueChangeOldKey")

; Declare the KVO callback procedure
DeclareC KVO(obj, sel, keyPath, object, change, context)

; Create Key-Value Observer class (PB_KVO)
Global KVO_Class.i = objc_allocateClassPair_(objc_getClass_("NSObject"), "PB_KVO", 0)
class_addMethod_(KVO_Class, sel_registerName_("observeValueForKeyPath:ofObject:change:context:"), @KVO(), "v@:@@@^v")
objc_registerClassPair_(KVO_Class)
 
; Create PB_KVO class instance (KVO)
Global KVO.i = CocoaMessage(0, 0, "PB_KVO new")

; >> End of Key-Value observer code <<<




Enumeration #PB_Event_FirstCustomValue
  #EventChangeAppearance
EndEnumeration

ProcedureC KVO(obj, sel, keyPath, object, change, context)
  Select PeekS(CocoaMessage(0, keyPath, "UTF8String"), -1, #PB_UTF8)
     
    Case "effectiveAppearance":
      CocoaMessage(0, 0, "NSAppearance setCurrentAppearance:", CocoaMessage(0, change, "objectForKey:", *NSKeyValueChangeNewKey\i))
      PostEvent(#EventChangeAppearance)
     
  EndSelect
EndProcedure

; add observer
Global NSApp.i = CocoaMessage(0, 0, "NSApplication sharedApplication")
CocoaMessage(0, NSApp, "addObserver:", KVO, "forKeyPath:$", @"effectiveAppearance", "options:", #NSKeyValueObservingOptionNew, "context:", #nil)
 

Procedure.i GetCocoaColor(ColorName.s) ; By wilbert: https://www.purebasic.fr/english/viewtopic.php?p=419571#p419571
  Protected.i Result, Rect.NSRect, Image, NSColor = CocoaMessage(#Null, #Null, "NSColor " + ColorName)
  If NSColor
    Rect\size\width = 1
    Rect\size\height = 1
    Image = CreateImage(#PB_Any, 1, 1)
    StartDrawing(ImageOutput(Image))
    CocoaMessage(#Null, NSColor, "drawSwatchInRect:@", @Rect)
    Result = Point(0, 0)
    StopDrawing()
    FreeImage(Image)
    ProcedureReturn Result
  Else
    ProcedureReturn -1
  EndIf
EndProcedure

; ---

Global Event, text_color, control_background_color

Procedure UpdateColors()
  text_color = GetCocoaColor("textColor")
  control_background_color = GetCocoaColor("controlBackgroundColor")
  If StartDrawing(CanvasOutput(0))
    Box(0, 0, OutputWidth(), OutputHeight(), control_background_color)
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawText(10, 10, "Hello World", text_color)
    StopDrawing()
  EndIf
EndProcedure

OpenWindow(0, #PB_Ignore, #PB_Ignore, 300, 150, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

CanvasGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)

UpdateColors()

Repeat
  Event = WaitWindowEvent()
  If Event = #EventChangeAppearance
    UpdateColors()
  EndIf
Until Event = #PB_Event_CloseWindow

; remove observer
CocoaMessage(0, NSApp, "removeObserver:", KVO, "forKeyPath:$", @"effectiveAppearance")

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Fri Apr 12, 2019 6:36 pm 
Offline
Addict
Addict
User avatar

Joined: Fri May 12, 2006 6:51 pm
Posts: 1760
Location: Germany
Thanks :D :wink:

_________________
My Projects ThreadToGUI / OOP-BaseClass / OOP-BaseClassDispatch / Event-Designer
PB v3.30 / v5.70 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Fri Apr 12, 2019 8:55 pm 
Offline
Enthusiast
Enthusiast

Joined: Thu Dec 29, 2011 5:03 pm
Posts: 434
Thank you, wilbert! That works great. I don't know how you manage all this stuff (I'm usually at a complete loss when trying to make something work with CocoaMessage), but I greatly appreciate it.


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Sat Apr 13, 2019 7:50 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3351
Location: Netherlands
wombats wrote:
I don't know how you manage all this stuff

Just googling and combining pieces :wink:

What the code does is observe for changes of the effectiveAppearance property of the application, set the appearance for the current thread the same and post an event.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Sat Apr 13, 2019 10:37 am 
Offline
Addict
Addict
User avatar

Joined: Fri May 12, 2006 6:51 pm
Posts: 1760
Location: Germany
I still find it difficult to implement some functions with CocoaMessage, but it always works better.

But how do you know that the Key Value Observer at Purebasic is called "PB_KV0" to connect it with your own "KVO" :?:

P.S.
The NSObject "PB_KVO" is created first. :?: :!:

_________________
My Projects ThreadToGUI / OOP-BaseClass / OOP-BaseClassDispatch / Event-Designer
PB v3.30 / v5.70 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Sat Apr 13, 2019 11:45 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3351
Location: Netherlands
mk-soft wrote:
But how do you know that the Key Value Observer at Purebasic is called "PB_KV0" to connect it with your own "KVO" :?:

It's just a name. "MKObserver" or any other class name not already used by the operating system or PureBasic will do.
You just need an object of a class that implements "observeValueForKeyPath:ofObject:change:context:" .

One way that is used by other code posted on the forum is to use the app delegate class for this and add the method to that.
But since I don't know if PureBasic itself already uses Key-Value observing (or will do so in the future), it's safer to create a new class instead of using the app delegate class for this.
Code:
objc_allocateClassPair_(objc_getClass_("NSObject"), "PB_KVO", 0)

creates a new class named PB_KVO which extends the NSObject class.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Sat Apr 13, 2019 2:06 pm 
Offline
Addict
Addict
User avatar

Joined: Fri May 12, 2006 6:51 pm
Posts: 1760
Location: Germany
Can the module "osxClassPair" also be used for other classes?
Code:
;-TOP

;- Begin Module

DeclareModule osxClassPair
  Declare CreateClassObject(ClassName.s, RegisterName.s, *Callback, Args.s)
  Declare DisposeClassObject(ClassName.s)
EndDeclareModule

; ---

Module osxClassPair
 
  ImportC ""
    objc_disposeClassPair(*Class)
  EndImport
 
  Structure udtClass
    *Class
    *Object
  EndStructure
 
  Global NewMap Class.udtClass()
 
  Procedure CreateClassObject(ClassName.s, RegisterName.s, *Callback, Args.s)
    Protected *Class, *Method, *Object
    *Class = objc_allocateClassPair_(objc_getClass_("NSObject"), ClassName, 0)
    If Not *Class
      ProcedureReturn 0
    EndIf
    *Method = class_addMethod_(*Class, sel_registerName_(RegisterName), *Callback, Args)
    If Not *Method
      objc_disposeClassPair(*Class)
      ProcedureReturn 0
    EndIf
    If Not objc_registerClassPair_(*Class)
      objc_disposeClassPair(*Class)
      ProcedureReturn 0
    EndIf
    *Object = CocoaMessage(0, 0, ClassName + " new")
    If Not *Object
      objc_disposeClassPair(*Class)
      ProcedureReturn 0
    EndIf
    Class(ClassName)\Class = *Class
    Class()\Object = *Object
    ProcedureReturn *Object
  EndProcedure
 
  Procedure DisposeClassObject(ClassName.s)
    If FindMapElement(Class(), ClassName)
      CocoaMessage(0, Class()\Object, "release")
      objc_disposeClassPair(Class()\Class)
      DeleteMapElement(Class())
    EndIf
  EndProcedure
 
EndModule

;- End Module

CompilerIf #PB_Compiler_IsMainFile
 
  UseModule osxClassPair
 
  EnumerationBinary
    #NSKeyValueObservingOptionNew
    #NSKeyValueObservingOptionOld
  EndEnumeration
 
  Global *NSKeyValueChangeNewKey.Integer = dlsym_(#RTLD_DEFAULT, "NSKeyValueChangeNewKey")
  Global *NSKeyValueChangeOldKey.Integer = dlsym_(#RTLD_DEFAULT, "NSKeyValueChangeOldKey")
 
  Enumeration #PB_Event_FirstCustomValue
    #EventChangeAppearance
  EndEnumeration
 
  ProcedureC KVO(obj, sel, keyPath, object, change, context)
    Select PeekS(CocoaMessage(0, keyPath, "UTF8String"), -1, #PB_UTF8)
       
      Case "effectiveAppearance":
        CocoaMessage(0, 0, "NSAppearance setCurrentAppearance:", CocoaMessage(0, change, "objectForKey:", *NSKeyValueChangeNewKey\i))
        PostEvent(#EventChangeAppearance)
       
    EndSelect
  EndProcedure
 
  ; Create Key-Value Observer class (PB_KVO)
  KVO = CreateClassObject("PB_KVO", "observeValueForKeyPath:ofObject:change:context:", @KVO(), "v@:@@@^v")
 
  ; add observer
  Global NSApp.i = CocoaMessage(0, 0, "NSApplication sharedApplication")
  CocoaMessage(0, NSApp, "addObserver:", KVO, "forKeyPath:$", @"effectiveAppearance", "options:", #NSKeyValueObservingOptionNew, "context:", #nil)
 
  Procedure.i GetCocoaColor(ColorName.s) ; By wilbert: https://www.purebasic.fr/english/viewtopic.php?p=419571#p419571
    Protected.i Result, Rect.NSRect, Image, NSColor = CocoaMessage(#Null, #Null, "NSColor " + ColorName)
    If NSColor
      Rect\size\width = 1
      Rect\size\height = 1
      Image = CreateImage(#PB_Any, 1, 1)
      StartDrawing(ImageOutput(Image))
      CocoaMessage(#Null, NSColor, "drawSwatchInRect:@", @Rect)
      Result = Point(0, 0)
      StopDrawing()
      FreeImage(Image)
      ProcedureReturn Result
    Else
      ProcedureReturn -1
    EndIf
  EndProcedure
 
  ; ---
 
  Global Event, text_color, control_background_color
 
  Procedure UpdateColors()
    text_color = GetCocoaColor("textColor")
    control_background_color = GetCocoaColor("controlBackgroundColor")
    If StartDrawing(CanvasOutput(0))
      Box(0, 0, OutputWidth(), OutputHeight(), control_background_color)
      DrawingMode(#PB_2DDrawing_Transparent)
      DrawText(10, 10, "Hello World", text_color)
      StopDrawing()
    EndIf
  EndProcedure
 
  OpenWindow(0, #PB_Ignore, #PB_Ignore, 300, 150, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
 
  CanvasGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
 
  UpdateColors()
 
  Repeat
    Event = WaitWindowEvent()
    If Event = #EventChangeAppearance
      UpdateColors()
    EndIf
  Until Event = #PB_Event_CloseWindow
 
  ; remove observer
 
  CocoaMessage(0, NSApp, "removeObserver:", KVO, "forKeyPath:$", @"effectiveAppearance")
  DisposeClassObject("PB_KVO")
 
CompilerEndIf

_________________
My Projects ThreadToGUI / OOP-BaseClass / OOP-BaseClassDispatch / Event-Designer
PB v3.30 / v5.70 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace


Top
 Profile  
Reply with quote  
 Post subject: Re: Detect change between Light and Dark Mode?
PostPosted: Sat Apr 13, 2019 3:06 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3351
Location: Netherlands
mk-soft wrote:
Can the module "osxClassPair" also be used for other classes?

I'm not sure what you exactly mean with using that module for other classes :?
For the current example, your code is fine.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye