Documentation
¶
Overview ¶
Package appkit is a pure-Go foundation for web-based desktop applications: it embeds the platform View (WKWebView on macOS, WebKitGTK on Linux, WebView2 on Windows) behind a single Go API and layers desktop services on top of it - windowing and app windows, drag regions, custom URL schemes, notifications, clipboard, single-instance, opening URLs and native file dialogs - all cgo-free.
Source layout: the package is split into three file families. app*.go holds the application scope - the App type (configuration + runtime context), its app-scope methods (Show, Wait, Copy/Paste, Open/Reveal, Notify) and the per-OS app internals (single-instance, app icon); view*.go holds the view/window API surface (the define-first View struct and its methods, the geometry + State/Config types, scheme types, App.Show glue, the View Dialog method, drag regions); the binding/events machinery may be split further into bind.go (registry model + value conversion), bind_gen.go (the generated JS) and bind_evt.go (the events bridge) - see AGENTS.md "Source layout"; lib*.go holds the pure per-platform engine layer (lib_{darwin,linux,windows}.go talk to WKWebView/WebKitGTK/WebView2 and the platform APIs).
Platform code lives in *_unix.go / *_windows.go / *_darwin.go files; a capability that a platform cannot provide returns an Err* sentinel or is a documented best-effort no-op rather than failing at compile time.
Index ¶
- Variables
- type App
- func (a *App) Autostart() *Autostart
- func (a *App) Backend() string
- func (a *App) Copy(b []byte) error
- func (a *App) Notify(title, message string) error
- func (a *App) Open(rawurl string) error
- func (a *App) Paste() ([]byte, error)
- func (a *App) Quit()
- func (a *App) Reveal(path string) error
- func (a *App) Show(view *View) error
- func (a *App) Wait() error
- type Autostart
- type State
- type View
- func (v *View) Close()
- func (v *View) Dialog(opts dialog.Options) ([]string, error)
- func (v *View) Emit(name string, data ...any) error
- func (v *View) Eval(js string)
- func (v *View) Focus(raise bool)
- func (v *View) Hide()
- func (v *View) Maximize()
- func (v *View) Minimize()
- func (v *View) Navigate(url string)
- func (v *View) Off(name string)
- func (v *View) On(name string, handler func(args ...json.RawMessage)) (cancel func())
- func (v *View) Show()
- func (v *View) Unmaximize()
- func (v *View) Unminimize()
- func (v *View) Window(f func(wnd unsafe.Pointer))
Constants ¶
This section is empty.
Variables ¶
var ErrAutostartNotSupported = errors.New("appkit: autostart is not supported on this platform")
ErrAutostartNotSupported is returned by Autostart methods when the current platform has no autostart backend.
var ErrScheme = errors.New("appkit: refused URL scheme")
ErrScheme is returned by Open when the URL's scheme is not in the allow-list.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct {
// Debug turns the platform web inspector / developer tools on for every
// window of this app (the app-wide default for View.Debug): set it once
// for "every window is debuggable". A view's own View.Debug ORs over it,
// and the APPKIT_DEBUG=1 environment variable forces the tools on for
// every view no matter what.
//
// Like every App field it is committed when the app scope opens (later
// edits have no effect) and it is read exactly once per view, at window
// creation. Backend mapping - WebView2 AreDevToolsEnabled (Windows),
// WebKitGTK enable-developer-extras (Linux), WKPreferences
// developerExtrasEnabled (macOS).
Debug bool
// Events names the JavaScript global the appkit events bridge installs on
// every page of this app: window.<Events> with on/off/emit (see
// View.On/Off/Emit). Empty (the default) uses the name "events". The name
// is fixed when each view is created.
Events string
// ID uniquely identifies this application - e.g.
// "com.github.malivvan.appkit" - the key the single-instance rule locks
// on. It is optional outside Single Instance Mode: with App.Exec nil it is
// unused and several instances of the same binary run side by side. When
// App.Exec is set (Single Instance Mode is on), ID is REQUIRED: a later
// launch of an app whose ID matches an already-running primary forwards
// its command-line arguments to that primary (see Exec) and exits
// quietly. Launching the binary with "--new-instance" in its arguments
// always starts a fresh instance, even in single-instance mode.
ID string
// Exec, when non-nil, ENABLES Single Instance Mode: only one process of
// this application runs at a time, and every later launch is redirected to
// it instead of starting a new process. App.ID must then uniquely identify
// the application (see the ID doc). With Exec nil (the default),
// single-instance mode is off and every launch runs its own process.
//
// Exec is invoked on the primary instance with the command-line arguments
// of each redirected later launch. It runs on its own goroutine, so hand
// the arguments to the UI thread if you touch UI state.
Exec func(args []string)
// Name is the application name, used where the OS asks for one - most
// visibly as the source shown by desktop notifications (App.Notify).
Name string
// Icon is a PNG image for the running application, applied on a
// best-effort basis wherever the platform supports it at runtime (macOS
// Dock, Linux GTK3/GTK4 window icons, ...). It is applied once, when the
// app scope opens and before the first window exists. When Icon is unset
// the embedded appkit mark is used instead (unexported; appkit applies it
// itself), so an appkit application always has a process face unless it
// brings its own. App.Icon is ALSO the source of the tray glyph when the
// app configures a tray (App.Tray) whose own Icon is unset: appkit
// downscales App.Icon (or the embedded mark) to a tray-sized PNG at app
// init. Unlike the per-window page icon it sets the face of the PROCESS; a
// stable, runtime process icon is intentionally a best-effort feature
// because it is hard to keep identical across all platforms. An unset icon
// is silently ignored; so are environments that cannot take a runtime icon
// (Windows reads the icon from the executable's own resources). Under an
// X11 window manager the icon is pushed per window; under Wayland, where
// the protocol has no per-window icons, GTK4 (>= 4.20) sends pixels via
// the xdg-toplevel-icon protocol, and the GTK3 stack installs a matching
// per-user .desktop entry and themed icon keyed to App.Name so the
// compositor's app_id lookup finds it. A PNG that cannot be decoded is
// never fatal.
Icon []byte
// Bind holds the application's declarative bindings: every entry is bound
// onto each view App.Show creates, so one entry here covers all windows.
// A key is a DOTTED path - dots separate nested variables on the page, so
// a value bound at "app.someAPI.call" appears as window.app.someAPI.call.
// What a value becomes is decided by its kind alone:
//
// - a function becomes a JS function the page calls. Its arity decides
// whether it ALSO works as a variable: a zero-argument function is a
// callable GETTER - call it (`window.name()`), or read it as a value
// (`await window.name`, which calls it with no arguments); a
// one-argument function is a callable SETTER - call it
// (`window.name(v)`), or ASSIGN to it (`window.name = v`, which runs
// it with the assigned value; the assignment expression yields that
// value, so await the CALL form for the result);
// - a length-2 array of two functions ([2]any{getter, setter}) becomes
// a readable AND writable property: reading it runs the getter over
// the bridge (`const v = await window.name`), assigning to it runs
// the setter (`window.name = v`) - see makeAccessorBinding;
// - any other value - a bool, a number, a string, or any JSON-encodable
// value such as a struct, map or slice - becomes an immutable JS
// constant bound wholesale under that name.
//
// The page's calls are dispatched to Go in the order it makes them, so a
// read issued after a write observes the write
// (`window.count = 1; await window.count`).
//
// No part of a Go type is ever bound separately: structs and maps are
// never walked. The namespace the bindings of a page are installed into
// is frozen once the batch finishes, so the page cannot mutate the
// functions, constants or accessor objects it was given.
//
// A nil entry binds nothing. A view may override an app-wide name - or
// unbind it with a nil entry - through its own View.Bind map.
//
// Entries are applied to every view deterministically: in alphabetical
// key order, before the view's own View.Bind entries (see App.Show), so
// the result never depends on Go's map iteration order.
//
// Like every App field it is committed when the app scope opens (later
// edits have no effect) and is read once per shown view, at window
// creation.
Bind map[string]any
// FS is the filesystem the application serves to its views - the app's
// content: HTML, CSS, scripts and anything else the page loads. When it is
// set (it is read once, when the app scope opens, like every App setting;
// later edits have no effect), every view shown by App.Show is served
// from it. The consumer never picks a serving mechanism: navigate the view
// to the uniform "app://" origin -
//
// w.Navigate("app://app/index.html")
//
// - and appkit serves the file at that path in the filesystem on every
// platform. The serving is scheme-first on Windows and Linux: WebView2's
// https vhost for the custom "app" scheme (whose responses carry the
// isolation headers), the registered custom scheme on Linux (WebKitGTK
// cannot attach the headers to scheme responses, so a scheme-served Linux
// page is not crossOriginIsolated - SharedArrayBuffer still works through
// the JSC_useSharedArrayBuffer option). macOS always serves over a
// temporary loopback http://localhost server (WKWebView cannot make a
// custom scheme a secure context, and a long-standing WebKit bug keeps
// SharedArrayBuffer off plain WKWebView pages); App.HTTP opts Linux and
// Windows into that same loopback origin. SharedArrayBuffer is available
// on every platform. A path without a file answers "not found". A nil FS serves
// no content - the window shows whatever the consumer navigates it to
// itself.
FS fs.FS
// HTTP serves the app's content over a TEMPORARY loopback http://localhost
// server instead of the platform's custom "app" scheme. On Linux and
// Windows it is an opt-in fallback - the native scheme serves them (see
// the FS doc), so only HTTP-opted windows load their app:// content from
// the temporary loopback origin; the server is torn down again once the
// first page load finishes. macOS always serves over the loopback origin
// (WKWebView cannot make a custom scheme a secure context and cannot
// provide SharedArrayBuffer on plain pages - a long-standing WebKit
// bug). Either
// way the consumer still navigates to the uniform "app://" origin, so
// HTTP is a purely internal serving choice. Every served response -
// loopback and vhost alike - carries the cross-origin-isolation headers
// (COOP/COEP + CORP), so every app page is cross-origin isolated and can
// use SharedArrayBuffer. Ignored when FS is nil.
HTTP bool
// Exit ends the application process when its last window closes: with
// Exit true, Wait returns as soon as the last window created with
// App.Show is gone. The default (false) keeps the process alive after the
// windows close - menu-bar/tray/background applications - and Wait then
// returns only when App.Quit is called.
Exit bool
// Tray optionally puts an icon with a menu in the system tray / menu bar
// for the life of the application. When set, the icon appears as App.Wait
// starts running the platform UI loop (that loop dispatches the tray's
// menu events) and is removed when Wait returns. It is the app-scoped,
// declarative form of the tray subpackage's Set/Remove pair; see the tray
// package for the full API.
//
// The Config is read once when the app scope opens - icon (PNG, plus
// dark-mode and macOS template variants), tooltip, tray-level click
// handlers and the whole menu tree (checkboxes, submenus, separators,
// per-item icons). When the config leaves its Icon unset, appkit derives
// the tray glyph at app init from App.Icon - falling back to the embedded
// appkit mark - downscaled to a tray-sized PNG (see the Icon doc). The
// tray is a launcher, not a live dashboard: the menu
// stays fixed for its lifetime, so there is no runtime update machinery.
//
// A menu item's OnClick runs on the UI thread; keep it short or hand the
// work to a goroutine. A typical use ends the app from the menu:
//
// Tray: &tray.Config{
// Icon: iconPNG,
// Tooltip: "my app",
// Items: []tray.Item{
// {Label: "Open", OnClick: openUI},
// {Label: "Quit", OnClick: app.Quit},
// },
// }
//
// On macOS the tray switches the application to the accessory (menu-bar)
// activation policy, so the app's Dock icon disappears while the tray is
// up; that is the tray package's behavior for standalone use too. A
// configured tray that cannot be created (for example a second tray in
// the same process) makes Wait fail with the tray package's error.
Tray *tray.Config
// contains filtered or unexported fields
}
App configures an appkit application and carries its runtime scope.
It is the single application-scoped object: the exported fields hold the application settings (content filesystem, tray, icon, single-instance, ...) and unexported fields hold the state of the scope (committed settings and the one-time platform initialization). It is conceptually similar to how http.Server holds configuration and context together.
The exported settings are read once - when the first App method is called - and are then committed: later edits to the fields do not affect the running application. That first call also performs the one-time platform initialization (ensureInit) before the requested action runs, so the call that opens the scope should come from the goroutine that will own the UI (the main goroutine).
All app-scoped services are methods on *App (Show, Wait, Copy, Open, Paste, Reveal, Notify, ...); App.Bind is a declarative map instead of a method - see its field doc. This is a deliberate design choice: the App scope is never hidden from the consumer.
func (*App) Autostart ¶
Autostart returns the application's autostart controller. The identifier a registration is stored under derives from App.ID when set, otherwise from a filesystem-safe slug of App.Name (or of the executable name when App.Name is empty) - see Autostart.Enable.
func (*App) Backend ¶
Backend reports which web-engine backend the App scope uses for its views, after the one-time platform initialization has run:
- "webkitgtk-6.0" or "webkit2gtk-4.1" on Linux - the stack that was actually loaded, honoring the APPKIT_BACKEND environment variable (see README "Linux shared libraries");
- "WKWebView" on macOS and "WebView2" on Windows, whose single built-in backend ignores the variable.
Like every App method it opens the scope first, so the returned name always matches the loaded backend rather than the requested one. It returns an empty string when the platform could not be initialized.
func (*App) Copy ¶
Copy puts b onto the system clipboard, replacing whatever was there. It is an App method over github.com/atotto/clipboard (which reads the platform clipboard through pbcopy/pbpaste on macOS, xclip/xsel/wl-copy on Linux and the Win32 API on Windows). Safe to call from any goroutine once the App scope is open.
The platform backends are text clipboards, so Copy is binary-safe only opportunistically: arbitrary bytes round-trip where the backend preserves them verbatim (the command-line tools treat the payload as opaque), while Windows maps the payload through its text clipboard and may not preserve non-text bytes. It returns an error when no clipboard backend is available (for example a headless Linux box without xclip/xsel/wl-copy).
func (*App) Notify ¶
Notify displays an OS-level notification with the given title and message, named after the app (App.Name). It delegates to the notify subpackage, which needs no window and no tray icon: each platform binds the notification service the OS ships - NSUserNotificationCenter on macOS, a Shell_NotifyIconW balloon on Windows, org.freedesktop.Notifications on Linux. The App method stays plain (title + message only, no options); for custom icons, urgency or an alert/beep use the notify subpackage's ShowOpts/Alert/Beep directly.
It is safe to call from any goroutine once the App scope is open and returns the notify package's ErrUnsupported on platforms without a backend (anything but macOS, Windows and Linux). On macOS it can return ErrUnavailable when the process has no notification center - the deprecated NSUserNotificationCenter needs a bundled .app the user granted Notification permission (see the notify package docs); check with errors.Is.
func (*App) Open ¶
Open opens rawurl with the user's default handler (browser, mail client, ...). Only http, https, mailto and file URLs are allowed; anything else - including a bare hostname or path with no scheme - returns ErrScheme. For a local file use a file:// URL, or Reveal to show it in the file manager.
func (*App) Paste ¶
Paste returns the current clipboard content as raw bytes. Text copied from other applications arrives as its UTF-8 encoding. An empty clipboard yields an empty slice with a nil error; an error is returned only when no clipboard backend is available.
func (*App) Quit ¶
func (a *App) Quit()
Quit asks a running application to terminate: Wait returns and the process may finish. It is safe to call from any goroutine (the UI loop is woken). Calling Quit before Wait is harmless - Wait then returns immediately.
func (*App) Reveal ¶
Reveal opens the platform file manager with path's location shown: Finder selects the file on macOS, Explorer selects it on Windows, and on Linux the containing folder is opened (selecting the file itself is file-manager specific and not portable). The path must exist.
func (*App) Show ¶
Show presents a configured View: the first time a View is shown, App.Show creates its window and web view (opens/commits the App scope, runs the one-time platform initialization) and registers the View with the App, which manages it from then on. Calling Show again on the SAME View while its window is alive brings it back instead of recreating it: the window is un-minimized, shown and focused (Focus(true)). After View.Close the View is unregistered and reset, so the same View can be Show'n again later.
The View's exported fields (Debug/FirstMouse, URL, Ready, the geometry fields Left/Top/Width/Height/State and the Bind map) are read once, exactly at the first Show; keep the *View afterwards - it is the handle to the shown window.
The first successful call pins the calling goroutine to its OS thread; keep all direct UI calls on that goroutine and re-enter through Window(func) from background goroutines. Exception: when the application run loop is already running (started by a tray loop or another owner), Show may be called from any goroutine - creation and the UI-touching methods marshal themselves to the main thread.
Every binding is applied while the window is created, deterministically: the app-wide App.Bind entries first, then the view's own View.Bind entries, each map iterated in alphabetical key order, so the outcome never depends on Go's map iteration order. Each entry is ONE name - a function value becomes a callable JS function, any other JSON-encodable value a frozen JS constant - and once the page's binding batch is installed the whole bound namespace is frozen (see makeBinding). A view entry overrides the same app name; a nil view entry unbinds it. The events bridge is installed before the page loads, so View.On/Off/Emit work immediately.
func (*App) Wait ¶
Wait blocks until the application exits. It is the app-level run loop: it opens the app scope once (committing the settings and performing the one-time platform initialization) and then runs the platform UI loop until the application should exit:
- App.Quit was called, or
- the last window created with App.Show was closed and App.Exit is true.
With Exit false (the default) the process keeps running after its windows are gone - menu-bar/tray/background applications - and only App.Quit (or the process being killed) ends it.
Create all windows with App.Show and keep every UI call on the goroutine that calls Wait (the main goroutine). A simple single-window app may skip Wait and call View.Run on its window instead; the two models must not be mixed.
type Autostart ¶
type Autostart struct {
// contains filtered or unexported fields
}
Autostart controls whether the application starts at user login. Get it from App.Autostart.
A registration points at the running executable and takes effect on the next login, not immediately. Re-enabling overwrites the registration; each platform keeps at most one entry per executable, so a previous registration under a different identifier (e.g. after App.Name changed) is replaced, not duplicated.
Enabled, Path and Backend report the CURRENT registration: they scan the platform's registration store for an entry whose command is the running executable, so they work regardless of the identifier used at Enable time.
func (*Autostart) Backend ¶
Backend returns the name of the mechanism the current registration uses: "xdg-autostart", "registry-run", "launchagent" or "smappservice". Empty when nothing is registered.
func (*Autostart) Disable ¶
Disable removes the autostart registration for the running executable. It is a no-op (nil error) when nothing is registered.
func (*Autostart) Enable ¶
Enable registers the application to launch at login with the given command line arguments (appended after the executable path). It is safe to call repeatedly: an existing registration is overwritten, and a stale entry pointing at this executable under a different identifier is removed first.
The registration identifier is App.ID when set; otherwise a slug of App.Name, or of the executable name when App.Name is empty. An App.ID containing characters outside A-Za-z0-9._- is rejected.
func (*Autostart) Enabled ¶
Enabled reports whether a registration for the running executable exists. It does not verify that a registered entry still points at the running binary; Disable and Enable always reconcile that themselves.
func (*Autostart) Path ¶
Path returns the path of the registration artefact for the current registration: the .desktop file path on Linux, the registry sub-key path (HKCU\…\Run\<id>) on Windows, the LaunchAgent plist path on macOS, or the bundle identifier for an SMAppService registration. Empty when nothing is registered.
type State ¶
type State int
State values configure window sizing and resizing at creation time.
const ( // StateNone lets the window size freely; zero Width/Height pick the // backend default. StateNone State = iota // StateMin makes Width and Height the minimum bounds. StateMin // StateMax makes Width and Height the maximum bounds. StateMax // StateFixed prevents the user from resizing the window. StateFixed )
type View ¶
type View struct {
// Debug turns the platform web inspector / developer tools on for this
// window. App.Show ORs it with the app-wide App.Debug - a true on either
// side (or the APPKIT_DEBUG=1 environment variable) opens the tools;
// nothing can turn them off while that environment variable is set.
//
// Backend mapping - WebView2 AreDevToolsEnabled (Windows), WebKitGTK
// enable-developer-extras (Linux), WKPreferences developerExtrasEnabled
// (macOS).
Debug bool
// FirstMouse makes a click on an INACTIVE window reach the page instead
// of only bringing the window forward.
//
// macOS only; ignored elsewhere, where a click on an inactive window
// already reaches the content. AppKit's default is the opposite of what
// most web UIs want: the first click is swallowed as activation, so a
// user who clicks a button in a window that lost focus has to click twice
// - and the first click looks broken. Turn this on for control panels,
// dashboards, players and anything else the user clicks in passing.
//
// It is OPT-IN because the default protects destructive interfaces: in a
// drawing tool, an editor, or any window with a delete button, a click
// that merely raises the window must NOT also press what happens to be
// under the cursor. Leave it off when a stray first click could destroy
// something.
FirstMouse bool
// URL is the page the window loads first. App.Show navigates the view
// here once the window exists (Navigate). The URL may be a
// uniform "app://" URL served by App.FS, an https:// URL, or a data: URI.
// Empty (the default) starts a blank window and no navigation happens -
// and because Ready fires on a completed load, it will not fire until a
// later Navigate completes.
URL string
// Ready, when non-nil, is called exactly once, on the UI thread, the
// first time a page finishes loading after Show (the initial Navigate to URL
// or the first later navigation). It is the "the window is fully up"
// callback: bindings and the events bridge are live by then.
//
// Pair it with Eval to run JavaScript on init: Ready fires only after the
// first page load completed, so the DOM and the page's own scripts are in
// place and a single Eval reaches them reliably. Injecting script before
// the document exists is not reliable across the three engines, so appkit
// has no declarative JS/CSS injection API - call Eval from Ready to run
// code when the page comes up.
Ready func()
// Bind holds this view's declarative bindings: every entry is bound onto
// the web view when App.Show runs. A key is a DOTTED path - dots
// separate nested variables on the page, so a value bound at
// "app.someAPI.call" appears as window.app.someAPI.call. What a value
// becomes is decided by its kind alone:
//
// - a function becomes a JS function the page calls. Its arity decides
// whether it ALSO works as a variable: a zero-argument function is a
// callable GETTER - call it (`window.name()`), or read it as a value
// (`await window.name`, which calls it with no arguments); a
// one-argument function is a callable SETTER - call it
// (`window.name(v)`), or ASSIGN to it (`window.name = v`, which runs
// it with the assigned value; the assignment expression yields that
// value, so await the CALL form for the result);
// - a length-2 array of two functions ([2]any{getter, setter}) becomes
// a readable AND writable property: reading it runs the getter over
// the bridge (`const v = await window.name`), assigning to it runs
// the setter (`window.name = v`);
// - any other value - a bool, a number, a string, or any JSON-encodable
// value such as a struct, map or slice - becomes an immutable JS
// constant bound wholesale under that name.
//
// The page's calls are dispatched to Go in the order it makes them, so a
// read issued after a write observes the write
// (`window.count = 1; await window.count`).
//
// No part of a Go type is ever bound separately: structs and maps are
// never walked. The namespace the bindings of a page are installed into
// is frozen once the batch finishes, so the page cannot mutate the
// functions, constants or accessor objects it was given.
//
// A name set here overrides the same name in the app-wide App.Bind map;
// a nil entry under a name UNBINDS that name again, removing an app-wide
// binding this view does not want. Entries are applied in alphabetical
// key order, after the app-wide entries (see App.Show). A nil entry in
// App.Bind itself binds nothing.
Bind map[string]any
// Frame creates the window with the OS frame - the title bar and system
// buttons - and an opaque background. The default (false) is a frameless
// window: NO OS decoration of any kind and a fully transparent background,
// so the desktop shows through everywhere the page does not paint. The
// page is then responsible for the window chrome and marks the movable
// pieces with the "-app-region" CSS attribute ("drag" / "no-drag");
// see the package documentation. Resizing still works from the window
// edges unless State is StateFixed.
//
// Only meaningful for windows appkit owns; an embedded window keeps its
// host's frame and background.
Frame bool
// Left/Top optionally place the window on the screen, in pixels. They are
// best effort, because not every window system lets a client pick its
// position: supported on Windows and on the GTK3/X11 stack; ignored on
// GTK4 and on Wayland (the compositor places windows). macOS treats the
// coordinates as AppKit screen coordinates (origin at the bottom-left).
// Both zero mean "let the platform decide".
Left, Top int
// Width/Height set the initial window size in pixels. Both zero mean the
// backend default (640x480), matching the behavior of a window that was
// never sized. With State StateMin/StateMax they are the respective
// minimum/maximum bounds instead.
Width, Height int
// State is the initial resize state (StateNone/StateMin/StateMax/
// StateFixed): StateFixed makes the window non-resizable (and disables the
// frameless edge resize), StateMin/StateMax turn Width/Height into bounds.
//
// Every geometry field above is applied once, when the window is created -
// see the note on the View type about window control.
State State
// contains filtered or unexported fields
}
View describes one window and its embedded web view. It is appkit's define-first window object, the same pattern App uses for the application: the exported fields are the configuration of a window that does not exist yet, and App.Show(view) turns it into a live window. Configure a View, hand it to App.Show, and keep the pointer - after Show the same View is the handle to its window (Navigate, On/Off/Emit, Show/Hide, ...), so there is no separate window object to track.
Show reads the fields exactly once, when the window is created; later edits have no effect on the running window (matching how App commits its own settings). Re-showing the same View while its window is alive brings the window back (see App.Show); after View.Close the fields are read again, so the View can be reconfigured and shown anew. Before Show the imperative methods below have no engine behind them and either return a clear error/zero value or panic with "View is not shown".
func (*View) Close ¶
func (v *View) Close()
Close tears the view down for good: it terminates the view's run loop (if one is running) and destroys the native window and web view, then UNREGISTERS the View from the App that showed it and resets its internal state (engine handle, App reference) so the same View can be App.Show'n again later. It is idempotent and safe to call from any goroutine, and it is a no-op before the View was ever shown - closing an unshown View is not an error.
func (*View) Dialog ¶
Dialog presents a native, application-modal file panel chosen by opts.Type (open, multi-open, save or directory) and built on the github.com/malivvan/appkit/dialog package. Unlike the other View methods it BLOCKS the calling goroutine until the user dismisses the dialog and therefore must NOT be called from the UI thread (doing so deadlocks). Call it from a Bind callback - which runs on a background goroutine - or any other goroutine. It requires the main loop to be running (Run has been called).
A cancelled dialog - and a dialog that could not be presented (no backend, no display) - returns an empty result and a nil error, per the dialog package contract.
func (*View) Emit ¶
Emit publishes an event to every listener on both sides. Each value in data becomes one argument delivered to the handlers (Go handlers receive it as raw JSON, JS handlers as a decoded value). It is safe to call from any goroutine; the JS-side listeners are notified on the UI thread. Emit returns an error only if a value in data cannot be JSON-encoded, in which case nothing is published.
func (*View) Eval ¶
Eval evaluates arbitrary JavaScript asynchronously; the result is ignored. It is the supported way to run code on init: call it from the View.Ready callback, once the first page load completed, so the DOM and the page's own scripts are in place. Injecting script before the document exists is not reliable across the three engines, so appkit exposes no declarative JS/CSS injection API.
func (*View) Focus ¶
Focus moves keyboard focus into the web content - so typing, and a screen reader's cursor, land inside the page - and, when raise is true, FIRST brings the window to the front and gives the application focus (the case a program that took focus away from itself needs: it launched a window that activates, finished a job that raised something else). Use Focus(true) sparingly - stealing focus from someone typing in another application is worse than the extra click it saves. Call it from the UI thread; the backends marshal to the UI thread when called from a background goroutine.
func (*View) Hide ¶
func (v *View) Hide()
Hide removes the window from the screen AND from the taskbar / window list - the classic "hide to tray" behavior: the process keeps running and the window stays alive until Show brings it back. Safe to call from any goroutine.
func (*View) Maximize ¶
func (v *View) Maximize()
Maximize enlarges the window to fill the available screen area. On macOS it performs the native zoom, which is a TOGGLE: calling Maximize on an already-zoomed window restores its previous size. Safe to call from any goroutine.
func (*View) Minimize ¶
func (v *View) Minimize()
Minimize shrinks the window to the taskbar / Dock (on macOS it is miniaturized into the Dock). Show restores it. Safe to call from any goroutine.
func (*View) Navigate ¶
Navigate loads the given URL in the view. The URL may be an "app://" URL served by App.FS, an https:// URL, a properly encoded data URI, or any other URL the platform engine accepts. Examples:
v.Navigate("https://github.com/malivvan/appkit")
v.Navigate("app://app/index.html")
v.Navigate("data:text/html,%3Ch1%3EHello%3C%2Fh1%3E")
func (*View) Off ¶
Off removes every Go handler subscribed to the named event. It does not affect the page's own JS listeners.
func (*View) On ¶
func (v *View) On(name string, handler func(args ...json.RawMessage)) (cancel func())
On subscribes handler to the named event and returns a function that cancels just this subscription. The handler receives the event's arguments, each as the raw JSON the emitter sent, to unmarshal into whatever type it expects. Handlers for a JS-originated event run on the binding goroutine; handlers for a Go-originated event run on the goroutine that called Emit. Re-enter the UI thread with Dispatch if a handler touches the window. Before Show the returned cancel is a no-op.
func (*View) Show ¶
func (v *View) Show()
Show makes the window visible again and brings it to the front, putting it back into the taskbar / window list after Hide, or restoring it after Minimize. Safe to call from any goroutine (the backends marshal to the UI thread).
func (*View) Unmaximize ¶
func (v *View) Unmaximize()
Unmaximize restores a maximized window to its previous normal size (the inverse of Maximize). It is a no-op when the window is not maximized. Safe to call from any goroutine.
func (*View) Unminimize ¶
func (v *View) Unminimize()
Unminimize restores a minimized window to its normal on-screen state (the inverse of Minimize). It is a no-op when the window is not minimized. Safe to call from any goroutine.
func (*View) Window ¶
Window marshals f to the UI thread and calls it with the view's native window handle (a GtkWindow* / NSWindow* / HWND). Use it instead of touching the platform from a background goroutine: it is the re-enter-the-UI-thread entry point (the former Dispatch) with the handle handed to you. f runs on the UI thread; keep it short. Before Show it panics with "View is not shown".
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Command demo is the single appkit showcase application.
|
Command demo is the single appkit showcase application. |
|
Package dialog shows the operating system's native open, save and choose-directory panels, cgo-free.
|
Package dialog shows the operating system's native open, save and choose-directory panels, cgo-free. |
|
demo
command
Command demo exercises the dialog subpackage end to end: it opens each of the four native panels - open, multi-select open, save-as and choose directory - one after another and prints what the user picked.
|
Command demo exercises the dialog subpackage end to end: it opens each of the four native panels - open, multi-select open, save-as and choose directory - one after another and prints what the user picked. |
|
Package notify displays OS-level desktop notifications without cgo or bundled libraries.
|
Package notify displays OS-level desktop notifications without cgo or bundled libraries. |
|
demo
command
Example notification demonstrates OS-level notifications through the notify subpackage.
|
Example notification demonstrates OS-level notifications through the notify subpackage. |
|
internal/buildtest
command
|
|
|
objc
Package objc is a low-level pure Go objective-c runtime.
|
Package objc is a low-level pure Go objective-c runtime. |
|
Package tray puts an icon with a menu in the system tray / menu bar, with no cgo and no bundled libraries.
|
Package tray puts an icon with a menu in the system tray / menu bar, with no cgo and no bundled libraries. |
|
demo
command
Basic tray example: one declarative tray icon with light/dark icon variants, tray-level click handlers, and a menu with a submenu, a checkbox and a notification item.
|
Basic tray example: one declarative tray icon with light/dark icon variants, tray-level click handlers, and a menu with a submenu, a checkbox and a notification item. |