System Events

A menu bar app lives for days. Nib tells it when the Mac sleeps and wakes, when the screen locks, when the system switches between light and dark, and when displays change, so the app can pause, refresh or restyle without polling.

Registering a callback

Assign the on_<event> property, exactly like on_appear:

import nib

def main(app: nib.App):
    app.title = "Uptime"
    status = nib.Text("Running")

    def paused():
        status.content = "Paused"

    def resumed():
        status.content = "Running"

    app.on_sleep = paused
    app.on_wake = resumed
    app.build(status)

nib.run(main)

Or use the event decorator, the way hotkey pairs with on_hotkey:

import nib

def main(app: nib.App):
    app.title = "Theme"
    label = nib.Text("Light")

    @app.event("appearance")
    def themed(dark: bool):
        label.content = "Dark" if dark else "Light"

    app.build(label)

nib.run(main)

Each event holds one callback: assigning again replaces it.

Events

Event Property Argument Fires when
sleep on_sleep none the Mac is about to sleep
wake on_wake none the Mac wakes from sleep
screen_lock on_screen_lock none the screen locks
screen_unlock on_screen_unlock none the screen unlocks
appearance on_appearance_change dark: bool the system switches between light and dark
displays on_displays_change none a display is connected, removed or rearranged
appear on_appear none the popover opens
disappear on_disappear none the popover closes

Behaviour

  • Callbacks run on the event thread, like button actions. Mutating views from them triggers the usual patch.
  • An exception in a callback is logged and does not stop the app.
  • Events fire whether or not the popover is open.
  • Under nib run, a reload re-runs main, so callbacks are registered again automatically.
  • To read the current state rather than wait for a change, use app.screen.get_dark_mode() and app.screen.list_displays().