Drag & Drop

Any container view in Nib supports drag-and-drop file handling through the on_drop parameter. When a user drags files or directories from Finder onto your view, Nib invokes your callback with a list of absolute file paths.

Basic usage

Pass an on_drop callback to any container view:

import nib

def main(app: nib.App):
    app.title = "Drop Zone"
    app.icon = nib.SFSymbol("arrow.down.doc")
    app.width = 300
    app.height = 250

    status = nib.Text("Drop files here", foreground_color=nib.Color.GRAY)

    def handle_drop(paths: list[str]):
        status.content = f"Received {len(paths)} file(s)"
        for path in paths:
            print(f"Dropped: {path}")

    app.build(
        nib.VStack(
            controls=[
                nib.SFSymbol("arrow.down.circle", font=nib.Font.system(48)),
                status,
            ],
            spacing=16,
            padding=32,
            on_drop=handle_drop,
            border_color="gray",
            border_width=1,
            corner_radius=12,
        )
    )

nib.run(main)

Callback signature

The on_drop callback receives a single argument. A list of absolute file system path strings:

def handle_drop(paths: list[str]):
    for path in paths:
        print(path)
        # e.g., "/Users/me/Desktop/report.pdf"

The paths are absolute and can point to either files or directories.

Supported container views

The on_drop parameter is available on all views that inherit from the base View class. Common choices for drop zones include:

  • VStack, HStack, ZStack
  • ScrollView
  • List
  • Group
  • Form
nib.HStack(
    controls=[nib.Text("Drop here")],
    on_drop=handle_drop,
    padding=24,
)

Working with dropped files

Reading file contents

def handle_drop(paths: list[str]):
    for path in paths:
        if path.endswith(".txt"):
            with open(path, "r") as f:
                content = f.read()
                text_view.content = content

Checking file types

import os

def handle_drop(paths: list[str]):
    images = [p for p in paths if p.lower().endswith((".png", ".jpg", ".jpeg", ".gif"))]
    if images:
        status.content = f"Received {len(images)} image(s)"
    else:
        status.content = "No images found"

Handling directories

import os

def handle_drop(paths: list[str]):
    for path in paths:
        if os.path.isdir(path):
            files = os.listdir(path)
            status.content = f"Folder with {len(files)} items"
        else:
            size = os.path.getsize(path)
            status.content = f"{os.path.basename(path)} ({size:,} bytes)"