首页 / Wails 入门教程 / 窗口管理 Window API

Wails 入门教程

窗口管理 Window API

本教程共 42 篇 · 第 19 篇 · 更新于 2026-08-03

Wails桌面开发窗口管理Window API运行时

19. 窗口管理 Window API

本节目标

  • 掌握 runtime.Window 包提供的窗口控制方法。
  • 能在 Go 和前端 TypeScript 里调用同一套窗口接口。
  • 实现标题修改、尺寸调整、位置移动、全屏与显隐等常见交互。

19-1 什么是 Window 运行时

窗口刚启动时,大小、标题都写在 options 里定死了。

可很多时候,我们要在程序跑起来之后再改窗口。

比如点击按钮让窗口居中,或者收进任务栏。

这些”跑起来再改”的动作,就交给 runtime.Window 包来做。

Note

runtime 是 Wails 的运行时包,分 Go 和 JavaScript 两套接口。Go 侧用 rt.WindowXxx(ctx, ...),前端用 window.runtime.WindowXxx(...),两边方法名一一对应,只差 Go 版本第一个参数是 context。学一个就会两个。

Go 侧先引入别名,避免和标准库 runtime 撞名:

import rt "github.com/wailsapp/wails/v2/pkg/runtime"

前端有两条路可走。第一条是直接用 JS 运行时,不用在 Go 里写任何绑定,方法名和 Go 侧完全一样:

import { WindowSetTitle } from "../wailsjs/runtime/runtime";

WindowSetTitle("新标题");

第二条是自己在 Go 里封一个方法再绑定给前端。适合”改标题的同时还要写日志、存配置”这类顺带要做后端事情的场景。默认模板里后端结构体是 App、包名是 main,调用路径就是:

await window.go.main.App.SetTitle("新标题");

下面把常用方法分组讲清楚,每个都给 Go 和前端两套写法。

19-2 改标题与显隐窗口

最常用的是改标题。Go 侧一行搞定:

func (a *App) SetTitle(title string) {
    rt.WindowSetTitle(a.ctx, title)
}

前端对应的 TypeScript 调用:

function rename() {
    window.go.main.App.SetTitle("码上学笔记");
}

隐藏和显示也很直接。想关掉窗口却留住程序,靠 WindowHideWindowShow

func (a *App) Hide() { rt.WindowHide(a.ctx) }
func (a *App) Show() { rt.WindowShow(a.ctx) }
Tip

做”最小化到托盘”的效果,可以先 Hide 窗口,等用户点托盘再 Show。配合下一章的 HideWindowOnClose 更顺手。

WindowIsNormal 告诉你窗口当前是不是普通状态。它只有在”不是最大化、不是最小化、也不是全屏”时才返回 true。

19-3 调尺寸与移动位置

调整窗口大小用 WindowSetSize,参数是宽和高:

func (a *App) Resize(w, h int) {
    rt.WindowSetSize(a.ctx, w, h)
}

读取当前大小用 WindowGetSize。Go 侧一次返回宽、高两个 int,前端返回的是 Promise<Size>,结构是 { w, h }

import { WindowGetSize } from "../wailsjs/runtime/runtime";

const size = await WindowGetSize();
console.log(size.w, size.h);

移动位置用 WindowSetPosition,坐标是相对当前显示器左上角的:

func (a *App) MoveTo(x, y int) {
    rt.WindowSetPosition(a.ctx, x, y)
}

读取位置返回 Position,结构是 { x, y }

最小和最大尺寸也能在运行时改。传 0, 0 就取消限制:

rt.WindowSetMinSize(a.ctx, 400, 300)
rt.WindowSetMaxSize(a.ctx, 1280, 800)

19-4 全屏、最大化与最小化

全屏切换有三件套:WindowFullscreenWindowUnfullscreenWindowIsFullscreen

func (a *App) ToggleFullscreen() {
    if rt.WindowIsFullscreen(a.ctx) {
        rt.WindowUnfullscreen(a.ctx)
    } else {
        rt.WindowFullscreen(a.ctx)
    }
}

最大化同样有开、关、查三态:

rt.WindowMaximise(a.ctx)        // 最大化
rt.WindowUnmaximise(a.ctx)      // 还原
rt.WindowIsMaximised(a.ctx)     // 是否最大化
rt.WindowToggleMaximise(a.ctx)  // 一键切换

最小化也一样:

rt.WindowMinimise(a.ctx)     // 最小化
rt.WindowUnminimise(a.ctx)   // 还原
rt.WindowIsMinimised(a.ctx)  // 是否最小化

让窗口回到屏幕正中,用 WindowCenter。它针对窗口当前所在的显示器生效:

rt.WindowCenter(a.ctx)

19-5 主题、置顶与打印

Windows 平台支持切换窗口主题。三个方法对应系统默认、浅色、深色:

rt.WindowSetSystemDefaultTheme(a.ctx)
rt.WindowSetLightTheme(a.ctx)
rt.WindowSetDarkTheme(a.ctx)
Warning

上面三个主题方法仅 Windows 有效。在 macOS 或 Linux 调用不会报错,也不会有任何效果。跨平台代码里要先判断系统。

让窗口始终盖在最上面,用 WindowSetAlwaysOnTop,参数是布尔值:

rt.WindowSetAlwaysOnTop(a.ctx, true)

运行时换背景色用 WindowSetBackgroundColour,四个参数是 R、G、B、A,取值范围都是 0~255。这个颜色会从所有透明像素底下透出来:

rt.WindowSetBackgroundColour(a.ctx, 0, 0, 0, 255) // 不透明黑
Note

Windows 上 Alpha 只认 0 和 255 两档,任何非 0 的值都会被当成 255。要做真正的半透明窗口,得配合下一章的 WebviewIsTransparentWindowIsTranslucent

想重新加载界面,有两个选择。WindowReload 只刷新当前页面,WindowReloadApp 会重建整个前端:

rt.WindowReload(a.ctx)     // 类似浏览器 F5
rt.WindowReloadApp(a.ctx)  // 重建前端资源

需要往页面注入一段 JS,用 WindowExecJS。它是异步执行,报错只会出现在浏览器控制台:

rt.WindowExecJS(a.ctx, "console.log('来自 Go 的脚本')")

最后,WindowPrint 会唤起系统打印对话框,适合做小票、报表类功能。

19-6 一个综合小例子

把前面这些方法串起来,才有体感。设想一个设置面板:用户点按钮就能切换窗口置顶、切换全屏、让窗口居中,或者暂时藏起窗口。

Go 侧把几个方法包成绑定:

func (a *App) TogglePin() {
    a.pinned = !a.pinned
    rt.WindowSetAlwaysOnTop(a.ctx, a.pinned)
}
func (a *App) CenterWindow() { rt.WindowCenter(a.ctx) }

前端放三个按钮,分别调 TogglePinToggleFullscreenCenterWindow。这样窗口控制完全在界面里完成,不用去碰系统菜单。

Tip

置顶这类”开/关”状态,存在结构体字段里,切换时取反最省事。前端想回显当前状态,再暴露一个 GetPinned 方法读出来就行。

19-7 方法速查表

最后给一张常用方法速查,方便回头翻:

功能Go 方法
改标题WindowSetTitle
显示 / 隐藏WindowShow / WindowHide
是否普通态WindowIsNormal
设尺寸WindowSetSize
读尺寸WindowGetSize
最小 / 最大尺寸WindowSetMinSize / WindowSetMaxSize
设位置WindowSetPosition
读位置WindowGetPosition
居中WindowCenter
全屏WindowFullscreen / WindowUnfullscreen / WindowIsFullscreen
最大化WindowMaximise / WindowUnmaximise / WindowToggleMaximise
最小化WindowMinimise / WindowUnminimise
置顶WindowSetAlwaysOnTop
背景色WindowSetBackgroundColour
主题(Windows)WindowSetSystemDefaultTheme / SetLightTheme / SetDarkTheme
打印WindowPrint
注入脚本WindowExecJS
刷新WindowReload / WindowReloadApp

前端对应的名字完全一致,只是换成 window.runtime.WindowXxx() 这条运行时路径(也可以从 ../wailsjs/runtime/runtime 里 import,有类型提示)。除了 WindowExecJS 只有 Go 侧提供,其余方法两边都有。

19-8 用 ExecJS 做主题切换

再讲一个 WindowExecJS 的实用场景:动态切深色模式。

前端把深色样式写在一个 class 上,Go 侧通过注入脚本去 toggle:

func (a *App) ToggleDark(dark bool) {
    if dark {
        rt.WindowExecJS(a.ctx, `document.body.classList.add('dark')`)
    } else {
        rt.WindowExecJS(a.ctx, `document.body.classList.remove('dark')`)
    }
}

WindowExecJS 是异步的,脚本里如果报错,只会在浏览器控制台出现,Go 侧收不到。所以它适合”下指令”,不适合”拿返回值”。

Warning

别用 WindowExecJS 去做需要返回值的复杂计算。要拿结果,应该在前端用 Promise 调 Go 的绑定方法,方向别反了。

Tip

注入的脚本字符串里如果含反引号或双引号,记得转义。复杂逻辑建议抽成前端函数,Go 只调一个简短的调用,比如 app.switchTheme('dark'),可读性高很多。

19-9 排错清单

写窗口控制时最常卡住的地方,列出来帮你省时间。

第一,前端调用忘了 await,结果读到的尺寸是上一帧的旧值。凡是返回 Promise 的,都加上 await

第二,拿不到 ctx。所有 Window 方法都要一个有效的 context.Context,它来自 OnStartup 或绑定方法接收的上下文。自己 context.Background() 造一个,窗口找不到目标,调用会静默失败。

第三,macOS 上主题方法没反应。那三个主题切换只在 Windows 有效,苹果和 Linux 调了也不会报错,只是没效果,别以为是代码写错。

第四,坐标对不上。前端拿到的 Position 是相对当前显示器的,和屏幕全局坐标不是一回事。做跨屏定位时要自己换算。

Tip

调试窗口行为,最方便是在 wails dev 下开浏览器开发者工具,直接在前端控制台敲 window.runtime.WindowGetSize() 看返回值,比反复改代码编译快得多。

常见误区

Warning

前端调用都返回 Promise。忘记 await 会导致顺序错乱,比如先读尺寸再改尺寸却拿到旧值。

Note

坐标 Position 和尺寸 Size 在前端是对象,不是数组。size.wpos.x 才是正确取法。

Tip

所有 Window 方法第一参数都是 context.Context。这个 ctx 来自 OnStartup 或绑定方法的接收,不要自己 context.Background() 硬凑,否则窗口找不到目标。