Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix light/dark mode for MacOS, and Retina icon support #176

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/pystray/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class Icon(object):
``darwin_nsapplication``
An ``NSApplication`` instance used to run the event loop. If this
is not specified, the shared application will be used.

``darwin_force_fullcolor``
If ``True``, the icon will be forced to use the full color image,
instead of automatically switching the icon color based on the
system appearance (dark or light mode).
"""
#: Whether this particular implementation has a default action that can be
#: invoked in a special way, such as clicking on the icon.
Expand Down
17 changes: 12 additions & 5 deletions lib/pystray/_darwin.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(self, *args, **kwargs):

self._status_item.button().setTarget_(self._delegate)
self._status_item.button().setAction_(self._ACTION_SELECTOR)
self._force_fullcolor = kwargs.get('darwin_force_fullcolor', False)

def _show(self):
self._assert_image()
Expand Down Expand Up @@ -159,18 +160,20 @@ def _assert_image(self):
"""Asserts that the cached icon image exists.
"""
thickness = self._status_bar.thickness()
size = (int(thickness), int(thickness))
if self._icon_image and self._icon_image.size() == size:
logical_size = (int(thickness), int(thickness))
retnia_size = thickness * 3
pixel_size = (int(retnia_size), int(retnia_size))
if self._icon_image and self._icon_image.size() == logical_size:
return

if self._icon.size == size:
if self._icon.size == pixel_size:
source = self._icon
else:
source = PIL.Image.new(
'RGBA',
size)
pixel_size)
source.paste(self._icon.resize(
size,
pixel_size,
PIL.Image.LANCZOS))

# Convert the PIL image to an NSImage
Expand All @@ -179,6 +182,10 @@ def _assert_image(self):
data = Foundation.NSData(b.getvalue())

self._icon_image = AppKit.NSImage.alloc().initWithData_(data)
self._icon_image.setSize_(logical_size)
# Template images match the taskbar appearance (dynamically light or dark). Uses the alpha channel only.
if not self._force_fullcolor:
self._icon_image.setTemplate_(True)
self._status_item.button().setImage_(self._icon_image)

def _create_menu(self, descriptors, callbacks):
Expand Down