quickmacapp 2025.3.16__tar.gz → 2025.3.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quickmacapp
3
- Version: 2025.3.16
3
+ Version: 2025.3.18
4
4
  Summary: Make it easier to write Mac apps in Python
5
5
  Description-Content-Type: text/x-rst
6
6
  License-File: LICENSE
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
  name = "quickmacapp"
10
10
  description = "Make it easier to write Mac apps in Python"
11
11
  readme = "README.rst"
12
- version = "2025.03.16"
12
+ version = "2025.03.18"
13
13
  dependencies = [
14
14
  "pyobjc-framework-Cocoa",
15
15
  "pyobjc-framework-ExceptionHandling",
@@ -1,5 +1,6 @@
1
1
  from ._quickapp import Actionable, Status, mainpoint, menu, quit
2
2
  from ._interactions import ask, choose, answer, getpass
3
+ from ._background import dockIconWhenVisible
3
4
 
4
5
  __all__ = [
5
6
  "Actionable",
@@ -11,4 +12,5 @@ __all__ = [
11
12
  "choose",
12
13
  "answer",
13
14
  "getpass",
15
+ "dockIconWhenVisible",
14
16
  ]
@@ -0,0 +1,155 @@
1
+ from typing import Any, Callable
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from AppKit import (
6
+ NSApplication,
7
+ NSApplicationActivateIgnoringOtherApps,
8
+ NSApplicationActivationPolicyAccessory,
9
+ NSApplicationActivationPolicyRegular,
10
+ NSLog,
11
+ NSNotification,
12
+ NSNotificationCenter,
13
+ NSRunningApplication,
14
+ NSWindow,
15
+ NSWindowWillCloseNotification,
16
+ NSWorkspace,
17
+ NSWorkspaceActiveSpaceDidChangeNotification,
18
+ NSWorkspaceApplicationKey,
19
+ NSWorkspaceDidActivateApplicationNotification,
20
+ NSWorkspaceDidHideApplicationNotification,
21
+ )
22
+
23
+
24
+ @dataclass
25
+ class SometimesBackground:
26
+ """
27
+ An application that is sometimes in the background but has a window that,
28
+ when visible, can own the menubar, become key, etc. However, when that
29
+ window is closed, we withdraw to the menu bar and continue running in the
30
+ background, as an accessory.
31
+ """
32
+
33
+ mainWindow: NSWindow
34
+ hideIconOnOtherSpaces: bool
35
+ onSpaceChange: Callable[[], None]
36
+ currentlyRegular: bool = False
37
+ previouslyActiveApp: NSRunningApplication = field(init=False)
38
+
39
+ def someApplicationActivated_(self, notification: Any) -> None:
40
+ # NSLog(f"active {notification} {__file__}")
41
+ whichApp = notification.userInfo()[NSWorkspaceApplicationKey]
42
+
43
+ if whichApp == NSRunningApplication.currentApplication():
44
+ if self.currentlyRegular:
45
+ # NSLog("show editor window")
46
+ self.mainWindow.setIsVisible_(True)
47
+ else:
48
+ # NSLog("reactivate workaround")
49
+ self.currentlyRegular = True
50
+ self.previouslyActiveApp.activateWithOptions_(
51
+ NSApplicationActivateIgnoringOtherApps
52
+ )
53
+ app = NSApplication.sharedApplication()
54
+ app.setActivationPolicy_(NSApplicationActivationPolicyRegular)
55
+ self.mainWindow.setIsVisible_(True)
56
+ from twisted.internet import reactor
57
+
58
+ reactor.callLater( # type:ignore[attr-defined]
59
+ 0.1, lambda: app.activateIgnoringOtherApps_(True)
60
+ )
61
+ else:
62
+ self.previouslyActiveApp = whichApp
63
+
64
+ def someApplicationHidden_(self, notification: Any) -> None:
65
+ """
66
+ An app was hidden.
67
+ """
68
+ whichApp = notification.userInfo()[NSWorkspaceApplicationKey]
69
+ if whichApp == NSRunningApplication.currentApplication():
70
+ # 'hide others' (and similar functionality) should *not* hide the
71
+ # progress window; that would obviate the whole point of having
72
+ # this app live in the background in order to maintain a constant
73
+ # presence in the user's visual field. however if we're being told
74
+ # to hide, don't ignore the user, hide the main window and retreat
75
+ # into the background as if we were closed.
76
+ self.mainWindow.close()
77
+ app = NSApplication.sharedApplication()
78
+ app.unhide_(self)
79
+
80
+ def someSpaceActivated_(self, notification: NSNotification) -> None:
81
+ """
82
+ Sometimes, fullscreen application stop getting the HUD overlay.
83
+ """
84
+ menuBarOwner = NSWorkspace.sharedWorkspace().menuBarOwningApplication()
85
+ # me = NSRunningApplication.currentApplication()
86
+ NSLog("space activated where allegedly %@ owns the menu bar", menuBarOwner)
87
+ if not self.mainWindow.isOnActiveSpace():
88
+ if self.hideIconOnOtherSpaces:
89
+ NSLog("I am not on the active space, closing the window")
90
+ self.mainWindow.close()
91
+ else:
92
+ NSLog("I am not on the active space, but that's OK, leaving window open.")
93
+ else:
94
+ NSLog("I am on the active space; not closing.")
95
+ self.onSpaceChange()
96
+
97
+ def someWindowWillClose_(self, notification: NSNotification) -> None:
98
+ """
99
+ The main window that we're observing will close.
100
+ """
101
+ if notification.object() == self.mainWindow:
102
+ self.currentlyRegular = False
103
+ NSApplication.sharedApplication().setActivationPolicy_(
104
+ NSApplicationActivationPolicyAccessory
105
+ )
106
+
107
+ def startObserving(self) -> None:
108
+ """
109
+ Attach the various callbacks.
110
+ """
111
+ NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
112
+ self, "someWindowWillClose:", NSWindowWillCloseNotification, None
113
+ )
114
+ wsnc = NSWorkspace.sharedWorkspace().notificationCenter()
115
+
116
+ self.previouslyActiveApp = (
117
+ NSWorkspace.sharedWorkspace().menuBarOwningApplication()
118
+ )
119
+
120
+ wsnc.addObserver_selector_name_object_(
121
+ self,
122
+ "someApplicationActivated:",
123
+ NSWorkspaceDidActivateApplicationNotification,
124
+ None,
125
+ )
126
+
127
+ wsnc.addObserver_selector_name_object_(
128
+ self,
129
+ "someApplicationHidden:",
130
+ NSWorkspaceDidHideApplicationNotification,
131
+ None,
132
+ )
133
+
134
+ wsnc.addObserver_selector_name_object_(
135
+ self,
136
+ "someSpaceActivated:",
137
+ NSWorkspaceActiveSpaceDidChangeNotification,
138
+ None,
139
+ )
140
+
141
+
142
+ def dockIconWhenVisible(
143
+ mainWindow: NSWindow,
144
+ hideIconOnOtherSpaces: bool = True,
145
+ onSpaceChange: Callable[[], None] = lambda: None,
146
+ ):
147
+ """
148
+ When the given main window is visible, we should have a dock icon (i.e.: be
149
+ NSApplicationActivationPolicyRegular). When our application is activated,
150
+ (i.e.: the user launches it from Spotlight, Finder, or similar) we should
151
+ make the window visible so that the dock icon appears. When that window is
152
+ then closed, or when our application is hidden, we should hide our dock
153
+ icon (i.e.: be NSApplicationActivationPolicyAccessory).
154
+ """
155
+ SometimesBackground(mainWindow, hideIconOnOtherSpaces, onSpaceChange).startObserving()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quickmacapp
3
- Version: 2025.3.16
3
+ Version: 2025.3.18
4
4
  Summary: Make it easier to write Mac apps in Python
5
5
  Description-Content-Type: text/x-rst
6
6
  License-File: LICENSE
@@ -2,6 +2,7 @@ LICENSE
2
2
  README.rst
3
3
  pyproject.toml
4
4
  src/quickmacapp/__init__.py
5
+ src/quickmacapp/_background.py
5
6
  src/quickmacapp/_interactions.py
6
7
  src/quickmacapp/_quickapp.py
7
8
  src/quickmacapp/py.typed
File without changes