Window.md
September 16, 2026 · View on GitHub
窗口类
use Kingbes\PebView\Window;
方法
创建一个窗口对象
构造函数
new Window创建一个窗口对象。 参数bool$debug是否开启debug模式 默认:true 返回Window返回一个窗口对象
用法:
$win = new Window(true);
销毁窗口
公共函数
destroy销毁窗口 参数- 无 返回
void无返回值
用法:
// 必须在 run() 方法之后调用
$win->destroy();
运行窗口
公共函数
run运行窗口 参数- 无 返回
Window返回窗口对象
用法:
// 必须在 setHtml() 或者 navigate() 方法之后调用
$win->run();
终止窗口
公共函数
terminate终止窗口 参数- 无 返回
void无返回值
用法:
// 用于关闭应用程序
$win->terminate();
分发
在具有运行/事件循环的线程上安排要调用的函数。 使用此功能例如可以用于与库或原生对象进行交互。
公共函数
dispatch分发函数 参数callable$callback要调用的函数 返回Window返回窗口对象
用法:
// 用于与库或原生对象进行交互
$win->dispatch(function($win, $arg) {
// 与库或原生对象进行交互
// $win 是窗口对象
// $arg 是传递给 dispatch() 方法的参数
});
设置窗口图标
windows 要求ico格式;Linux 要求png格式;MacOs 要求ico格式 一般只有Windows 系统会奏效,Linux和macox并没有这个效果
公共函数
setIcon设置窗口图标 参数string$path图标文件路径 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用
$win->setIcon("path/to/icon.ico");
设置标题栏外观
设置原生标题栏的浅色 / 深色以及配色。不调用时保持系统默认外观,行为与以前完全一致。
各平台支持范围不同,不支持的组合会抛异常,而不是静默无效:
| 平台 | 浅色 / 深色 | 自定义配色 |
|---|---|---|
| Windows | 支持 | 支持(需要 Win11;Win10 只认浅色 / 深色) |
| macOS | 支持 | 不支持,标题栏由系统绘制,AppKit 没有公开接口 |
| Linux | 不支持,无法只改标题栏深浅(GTK 改主题是全局的) | 支持,会切换为 CSD 自绘标题栏(GtkHeaderBar + CSS) |
公共函数
setTitlebarTheme设置标题栏外观 参数?bool$darktrue 深色,false 浅色,null 跟随系统(默认 null)?string$caption标题栏底色,#RRGGBB或RRGGBB,null 用系统默认(默认 null)?string$text标题栏文字色,格式同上,null 按深色 / 浅色自动选择(默认 null) 返回Window返回窗口对象
用法:
// 跟随系统
$win->setTitlebarTheme();
// 只要深色标题栏
$win->setTitlebarTheme(dark: true);
// 自定义配色(Windows 11 / Linux CSD)
$win->setTitlebarTheme(dark: true, caption: "#1F2430", text: "#FFD166");
颜色格式非法会抛 InvalidArgumentException;窗口不存在或当前平台不支持该组合会抛 RuntimeException。
自定义标题栏(真正无边框 + JS 驱动缩放)
这一节和上一节是两回事:上一节只改标题栏的颜色 / 深浅,标题栏本身还是系统画的; 这一节把标题栏让出来,由你用 HTML 自己画。
打开之后,窗口顶部 $height 像素改由你用 HTML 绘制,系统不再画标题栏;
窗口真正无边框——WM_NCCALCSIZE 直接 return 0,客户区填满整个窗口,内容贴到窗口顶边、
零顶部默认边距(不留任何会自动缩放的边框)。边缘调整大小改由 JS 在 mousedown 里
判定边缘、调 beginResize() 发起(见下),因为 WebView2 子窗口吃掉鼠标、系统命中测试抓不到边缘。
切换自定义标题栏不会改变客户区尺寸:setSize() 说的始终是内容区尺寸,
开/关前后拿到的客户区一样大(内部按 caption 的差值补偿了窗口外框)。
各平台实现与差异:
| 平台 | 做法 | 拖动 | 说明 |
|---|---|---|---|
| Windows | WM_NCCALCSIZE return 0,客户区填满整窗(真正无边框、零顶部边距) | 由 beginDrag() 统一发起 | 边缘缩放由 beginResize() 统一发起(JS 判定边缘);双击最大化 / Aero 贴边原生生效 |
| macOS | 标题栏透明化 + 内容下延(FullSizeContentView) | 系统处理 | 红绿灯与拖动都是原生的;$controlsWidth 无意义(红绿灯位置由系统决定) |
| Linux | gtk_window_set_decorated(FALSE) | 需要 JS 配合 | GTK 收不到被 webview 吃掉的鼠标事件,要在 mousedown 里调 beginDrag() |
三平台同一句 JS:拖动由
beginDrag()发起、缩放由beginResize(edge)发起,页面侧都不按平台分支。 WebView2 把网页内容放在一串铺满客户区的子窗口里(webview_widget → Chrome_WidgetWin_* → Chrome_RenderWidgetHostHWND),鼠标事件被子窗口认领,宿主窗口的WM_NCHITTEST根本不会被系统问到 —— 所以不能靠命中测试认领标题栏 / 边缘区域。JS 在标题栏mousedown里把event.screenX / screenY传给beginDrag(),在四条边/角mousedown里把判定出的edge传给beginResize(),由各平台原生侧 按最合适的方式发起(Windows 拖动等价合成WM_NCLBUTTONDOWN(HTCAPTION)、缩放发WM_SYSCOMMAND(SC_SIZE|edge), 都进入系统模态循环,贴边 / 双击最大化原生)。按钮区用stopPropagation挡掉mousedown,$controlsWidth仅作保留参数。
公共函数
-
setCustomTitlebar启用自定义标题栏 参数 -
?int$height标题栏高度(px)。null或<= 0表示关闭、退回系统标题栏 -
int$controlsWidth右侧按钮区宽度(px),该区域内不响应拖动;0 表示整条可拖。 仅作保留参数,按钮排除由 webview 侧stopPropagation处理 -
bool$drag标题栏区域是否可拖动 返回 -
Window返回窗口对象 -
minimize最小化窗口 返回 -
Window返回窗口对象 -
toggleMaximize最大化 / 还原(切换) 返回 -
bool切换之后是否处于最大化 -
isMaximized当前是否处于最大化 返回 -
bool -
onStateChange监听最大化 / 还原(只在状态真的变化时回调一次) 参数 -
callable$callable形如function (Window $win, bool $maximized): void返回 -
Window返回窗口对象 -
beginDrag发起窗口拖动(屏幕坐标)。三平台统一:Windows 合成WM_NCLBUTTONDOWN(HTCAPTION), Linux 用gdk_window_begin_move_drag,macOS 按光标位移搬窗口。返回self,窗口存在时不会抛异常; 左键已松开返回4(当作 no-op)。 参数 -
int$x屏幕坐标 X -
int$y屏幕坐标 Y -
beginResize发起窗口缩放(边/角编码)。与beginDrag同一思路,因为 WebView2 子窗口吃掉鼠标、 系统命中测试抓不到边缘,所以缩放由 JS 在mousedown里判定边缘后发起。三平台统一:Windows 发WM_SYSCOMMAND(SC_SIZE | edge),Linux 用gdk_window_begin_resize_drag,macOS 按光标位移改窗口 frame。 返回self,窗口存在时不会抛异常;左键已松开返回4(当作 no-op)。 参数 -
int$edge边/角编码(WMSZ_*):1=左 2=右 3=上 4=左上 5=右上 6=下 7=左下 8=右下
用法:
$win->setCustomTitlebar(36, 138); // 36px 高,右侧 138px 留给三个按钮
// 按钮直接走现有的 bind,不需要新 API
$win->bind('tbMinimize', fn() => $win->minimize());
$win->bind('tbMaximize', fn() => $win->toggleMaximize());
$win->bind('tbClose', fn() => $win->terminate());
$win->bind('tbIsMax', fn() => $win->isMaximized());
// 最大化时窗口会有圆角与内缩,标题栏要跟着调整
$win->onStateChange(function ($win, $maximized) {
$win->eval('window.__onMaximized(' . ($maximized ? 'true' : 'false') . ')');
});
// 拖动统一由 JS 发起(三平台同一句),这里转调 beginDrag
$win->bind('tbBeginDrag', function (int $x, int $y) use ($win) {
$win->beginDrag($x, $y); // 在页面 mousedown 里传 event.screenX / screenY
return true;
});
// 缩放也由 JS 发起:页面在四条边/角 mousedown 里判定出 edge,这里转调 beginResize
$win->bind('tbBeginResize', function (int $edge) use ($win) {
$win->beginResize($edge); // edge 编码 1..8(WMSZ_*:左/右/上/左上/右上/下/左下/右下)
return true;
});
页面里那条标题栏(三平台都能拖,按钮用 stopPropagation 退出拖动);四条边/角在 mousedown 里
判定 edge 后调 tbBeginResize(edge)(缩放也由 JS 统一发起,不按平台分支):
<!-- 高度要等于 setCustomTitlebar() 的 $height -->
<div id="bar" style="height:36px; cursor:move">
<span>自己的标题</span>
<!-- 按钮区:在 mousedown 上 stopPropagation 退出拖动/缩放(见 demo) -->
<div id="controls">
<button onclick="tbMinimize()">-</button>
<button onclick="tbMaximize()">□</button>
<button onclick="tbClose()">×</button>
</div>
</div>
<script>
const M = 6; // 缩放区宽度(px)
// 由坐标算出落在哪条边/角(WMSZ_* 编码,与 C 侧一致):0=不在边缘
function edgeOf(x, y) {
const w = innerWidth, h = innerHeight;
const L = x < M, R = x > w - M, T = y < M, B = y > h - M;
if (T && L) return 4; if (T && R) return 5; if (B && L) return 7; if (B && R) return 8;
if (L) return 1; if (R) return 2; if (T) return 3; if (B) return 6; return 0;
}
// 标题栏 mousedown 拖动;四条边/角 mousedown 缩放(按钮区已 stopPropagation)
document.body.addEventListener('mousedown', e => {
if (e.button !== 0) return;
if (e.target.closest('#bar') || e.target.closest('#controls')) return;
const edge = edgeOf(e.clientX, e.clientY);
if (edge) tbBeginResize(edge);
});
const bar = document.getElementById('bar');
bar.addEventListener('mousedown', e => { if (e.button === 0) tbBeginDrag(e.screenX, e.screenY); });
bar.addEventListener('dblclick', () => tbMaximize());
</script>
完整示例见 test/demo-custom-titlebar.php。
$height必须和页面里那条标题栏的实际高度一致 —— 这个数字决定系统让出多少像素。拖动走
beginDrag()、缩放走beginResize(edge),页面侧都不按平台分支;$controlsWidth只是保留参数, 按钮排除在 webview 侧用stopPropagation处理。关闭($height传 null)后窗口 会完整退回系统标题栏,可以随时开关。
设置窗口标题
公共函数
setTitle设置窗口标题 参数string$title窗口标题名称 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用
$win->setTitle("PebView");
设置窗口大小
公共函数
setSize设置窗口大小 参数int$width窗口宽度int$height窗口高度WindowHint$hint窗口提示 默认 - WindowHint::None 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用
$win->setSize(800, 600, WindowHint::None);
初始化js
会在window.onload之前加载js代码
公共函数
init初始化js 参数string$jsjs代码 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用
$win->init("console.log('hello PebView!');");
执行js代码
公共函数
eval执行js代码 参数string$jsjs代码 返回Window返回窗口对象
用法:
$win->eval("console.log('hello PebView!');");
设置窗口HTML内容
公共函数
setHtml设置窗口HTML内容 参数string$htmlHTML内容 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用 和 navigate() 方法 只能选一个
$win->setHtml("<h1>hello PebView!</h1>");
导航窗口到指定url
公共函数
navigate导航窗口到指定url 参数string$urlurl地址 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用 和 setHtml() 方法 只能选一个
$win->navigate("https://www.baidu.com");
绑定js函数
公共函数
bind绑定js函数 参数string$name函数名称callable$callback要调用的函数 返回Window返回窗口对象
用法:
$win->bind("hello", function(...$params) {
// $win 是窗口对象
// $params 是传递给 hello() 方法的参数
// 可以使用 $params[0], $params[1], ... 来访问参数
// 例如:$params[0] 是第一个参数,$params[1] 是第二个参数,以此类推
return $params[0] . " " . $params[1];
});
解绑js函数
公共函数
unBind解绑js函数 参数string$name函数名称 返回Window返回窗口对象
用法:
$win->unBind("hello");
设置窗口关闭事件
公共函数
setCloseCallback设置窗口关闭事件 参数callable$callback要调用的函数 返回Window返回窗口对象
用法:
$win->setCloseCallback(function($win) {
// $win 是窗口对象
// 可以在关闭窗口时执行一些操作
return true; // 返回 true 表示允许关闭窗口
return false; // 返回 false 表示不允许关闭窗口
});
窗口显示
公共函数
show窗口显示 参数- 无 返回
Window返回窗口对象
用法:
$win->show();
窗口隐藏
公共函数
hide窗口隐藏 参数- 无 返回
Window返回窗口对象
用法:
$win->hide();
创建托盘
windows 要求ico格式;Linux 要求png格式;MacOs 要求ico格式
公共函数
tray创建托盘 参数string$path图标文件路径 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用
$win->tray("path/to/icon.ico");
托盘菜单
公共函数
trayMenu创建托盘菜单 参数array$menu菜单数组 返回Window返回窗口对象
用法:
// 必须在 run() 方法之前调用
// 注意:每个菜单项本身是一个数组,整体是"数组的数组"
$win->trayMenu([
[
"text" => "menu1", // 菜单名称
"disabled" => 0, // 0 可点击,1 禁用
"checked" => 0, // 0 未勾选,1 勾选
"cb" => function ($win) { // 点击菜单时调用的函数
// $win 是窗口对象
// 可以在点击菜单时执行一些操作
},
],
[
"text" => "menu2",
"cb" => function ($win) {
// $win 是窗口对象
// 可以在点击菜单时执行一些操作
},
],
]);