【CStackGUI 入门 06】主窗口与窗口事件:一页配置、19 种事件、程序信息
发布时间:2026/9/28 4:41:26来源:尧图网络
文章目录一、前五课讲控件这一课讲窗口二、跑起来 体检报告三、窗口配置一页 .cgui 就够四、程序信息app.info 真的进 exe五、窗口事件19 个写在 app.events 里六、窗口级 API七、踩坑清单本课全部踩过一遍八、验收九、下一篇一、前五课讲控件这一课讲窗口前五课把零件都过了一遍基础控件、输入、数值、数据、命令。但有个角色一直没正式出场 ——窗口自己。它可不是装控件的容器这么简单它有一整套配置标题、尺寸、底色、是否居中、边框样式可调/固定/对话框/无边框、总在最前、要不要在任务栏、Esc 关不关窗、有没有托盘图标、窗口图标、鼠标指针……它有自己的事件就绪、关闭、尺寸变化、按键、输入字符、鼠标、空闲、托盘、显示隐藏……一共 19 个它还有一份程序信息产品名、版本、作者、单位、版权 —— 这份信息会被写进 exe 的版本信息里。这一课把这三块一次讲清。核心结论先说窗口本身也是一个控件伪控件 id 固定是__window它的配置写在.cgui的app里事件写在app.events里和控件事件共用同一套绑定机制。下面这张图把窗口的三大块串起来看窗口 伪控件 __window配置 app标题/尺寸/边框/托盘…事件 app.eventsready/close/size/keydown…程序信息 app.info产品名/版本/作者/版权写进 .cgui 文件编译进 exe运行时 C 回调二、跑起来 体检报告build-app.bat lesson06_window examples\lesson06_window\lesson06_window.exe :: 自检 examples\lesson06_window\lesson06_window.exe --selftest examples\lesson06_window\lesson06_window.cgui -- 结果: 控件 13 个, 触发回调 4 次, 未登记 0 个, 事件不支持 0 个, 绑定出错 0 个 -- selftest OK为什么 13 个控件只有 4 次回调因为这 4 次全是按钮的click——--selftest不开窗所以窗口级事件ready/size/keydown…不会被合成触发。也就是说窗口事件只能真跑起来验这正是这一课的价值 —— 它跑起来之后自己就能证明自己日志第 1 行是ready第 3 行是size客户区变成 760×436启动时窗口定尺寸就发了一次状态栏第三格空闲 N 次说明idle正在跑。三、窗口配置一页.cgui就够app:{title:入门 06 · 主窗口与窗口事件,width:760,height:436,bg:#F3EDF7,center:true,escClose:false,tray:true,events:{ready:on_app_ready,close:on_app_close,…:…},info:{product:…,version:…}}app里能配的全部字段不写的字段一律保持 Windows 默认行为所以老文件不受影响字段默认说明x/y系统决定窗口左上角屏幕坐标逻辑像素centerfalse启动时按当前显示器工作区居中盖过x/ybordersizablesizable可调整 /fixed普通固定 /dialog对话框固定 /none无边框controlBoxtrue是否显示标题栏按钮组系统菜单 关闭钮minimizeBox/maximizeBoxtrue是否显示最小化 / 最大化按钮topmostfalse总在最前taskbartrue是否在任务栏显示visibletrue启动即显示false时先隐藏等业务cg_win_show()enabledtruefalse 整窗禁用点了没反应enterTabfalse回车当 Tab 下移焦点默认回车触发submitescClosetrueEsc 关窗关掉后 Esc 会当成当前控件的submittrayfalse建系统托盘图标用窗口图标并触发tray事件icon运行时自带窗口图标.ico路径cursorarrow窗口鼠标指针hand / ibeam / wait / cross / sizeall … 共 13 种classCStackGUIMain注册 Win32 窗口类用的名字一般不用改这些都是一行 C 都不用写的部分—— 想让窗口变成对话框样式、去掉最大化按钮、开机启动就居中改.cgui即可不用重新编译。四、程序信息app.info真的进 exeinfo:{product:CStackGUI 主窗口示例,desc:入门 06窗口配置、窗口事件、程序信息,version:1.0.0.0,build:2026-09-23,author:CStackGUI,company:CStackGUI,copyright:© 2026 CStackGUI示例,email:demoexample.com}这份信息不是给画布看的注释它有两条实在的用处编译时会落进 exe 的版本信息把鼠标停在你编出来的 exe 上右键 → 属性 → 详细信息产品名、版本、作者、版权都在那儿 —— 发给别人时对方能一眼看清这是什么程序运行时能用 C 读回来constchar*namecg_app_info(app,product);/* 键见 docs\CGUI-FORMAT.md 1.2 节取不到返回 NULL */本课顶部那一排就是这么填的ready回调里读一次写进四个标签。可读的键一共 13 个product/desc/comments/version/build/author/company/copyright/address/postcode/phone/fax/email/homepage。顺带一个实用小技巧把cg_app_info拼起来就是关于框的内容 ——cg_message_box(self,text,关于,0);/* 0 信息 / 1 警告 / 2 错误 / 3 问号 */五、窗口事件19 个写在app.events里app:{events:{ready:on_app_ready,close:on_app_close,size:on_win_size,keydown:on_win_keydown,char:on_win_char,mousedown:on_win_mousedown,idle:on_app_idle,tray:on_app_tray}}回调签名和控件事件完全一样self就是窗口伪控件staticvoidon_win_size(cg_widget_t*self,cg_event_tev,void*user);窗口支持的全部 19 个事件事件什么时候发参数怎么取ready窗口与控件都建好、还没显示启动初始化放这里close/closequery用户点关闭或 AltF4拦下来cg_cancel_close(app)destroy窗口已销毁、进程马上退出—move/size位置 /客户区尺寸变了cg_event_x 宽、cg_event_y 高activate/deactivate/firstactivate被激活 / 取消激活 / 首次激活—idle消息队列空着别在这里做耗时活—tray托盘图标被点cg_tray_action()1 单击 / 2 双击 / 3 右键show/hide被显示 / 被隐藏—mousedown/mouseup/rdown/rup/mousemove客户区鼠标动作cg_event_x/y()客户区坐标dblclick客户区双击同上gotfocus/lostfocus窗口获得 / 失去键盘焦点—keydown/keyup按下 / 放开按键cg_event_key()虚拟键码、cg_event_keys()功能键位或1 Ctrl / 2 Shift / 4 Altchar输入了可打印字符中文输入法也走这里cg_event_key() 字符码wheel滚轮cg_event_delta()正数向上本课演示了其中 8 个每个都往事件流水列表写一行列表用cg_widget_list_scroll_bottom()跟着滚staticvoidon_win_keydown(cg_widget_t*self,cg_event_tev,void*user){intvkcg_event_key(self),keyscg_event_keys(self);logev(cg_app_of(self),keydown键码 0x%02X%s%s%s,vk,(keys1)? Ctrl:,(keys2)? Shift:,(keys4)? Alt:);}窗口从启动到关闭的完整事件时序你的回调窗口伪控件Windows你的回调窗口伪控件Windowsloop[运行期]创建窗口与控件ready启动初始化定尺寸size客户区 760×436键盘/鼠标/空闲…keydown / mousedown / idle…处理并刷新界面用户点关闭closequery可 cg_cancel_close 拦截closedestroy进程退出六、窗口级 API想干什么用哪个居中 / 挪位置 / 改尺寸cg_win_center(app)/cg_win_set_pos(app,x,y)/cg_win_set_size(app,w,h)显示、隐藏cg_win_show(app, 1/0)置顶开关cg_win_set_topmost(app, on)最大化 / 最小化 / 还原cg_win_set_state(app, max/min/normal)换窗口图标cg_win_set_icon(app, app.ico)拦下关闭cg_cancel_close(app)只在close/closequery里有效读程序信息cg_app_info(app, product)托盘动作cg_tray_action(self)延时/定时cg_set_timer(app, delay_ms, fn, user)——一次性的周期性任务要自己重挂有没有窗口自检判断cg_app_has_window(app)本课的隐藏 1 秒再显示就是这两句cg_win_show(app,0);/* 隐藏会触发 hide 事件 */cg_set_timer(app,1000,show_again,app);/* 一秒后拉回来触发 show 事件 */七、踩坑清单本课全部踩过一遍--selftest不合成窗口事件。窗口配置写错了、事件名写错了自检不一定报——它只检查名字是否登记“控件是否支持”。窗口级的东西只能真跑起来看。托盘事件要先开字段。app.events里写了tray: on_tray但app里没写tray: true托盘图标根本不会建那个回调就是死代码写这一课时我自己就漏了运行时是if (app-doc.tray) cg_tray_set(...)字段不开就没图标。idle里别做耗时活。它触发极其频繁消息队列一空就来做重活会把界面拖死长时间的后台活应该用cg_set_timer()定周期做。本课只数个数、每 50 次刷一下状态栏。cg_set_timer()是一次性的到点触发后自己回收不会自动重挂。弹框前先问cg_app_has_window(app)--selftest不开窗消息框会把自检卡死这条是全框架通用。size事件里别指望控件重排运行时没有布局引擎窗口变了尺寸控件还是原来的绝对坐标。move/size/idle/mousemove都很频繁要在这里做的事尽量轻或者做成攒够 N 次才处理一次。把本课的坑按类型归个类踩坑清单自检盲区配置联动性能陷阱生命周期--selftest 不合成窗口事件窗口级只能真跑验弹框前先问 cg_app_has_window托盘事件要先开 tray 字段否则回调是死代码idle 里别做耗时活move/size/mousemove 频繁攒够 N 次再处理cg_set_timer 一次性要自己重挂size 事件里别指望控件重排八、验收examples\lesson06_window\lesson06_window.exe --selftest examples\lesson06_window\lesson06_window.cgui :: 期望控件 13 个, 触发回调 4 次, 未登记 0 个, 事件不支持 0 个, 绑定出错 0 个再加一条手动验收自检做不到的部分跑起来后拖一下窗口边框、敲几个键、点一下客户区、点一下「窗口居中」看那张流水表是不是每一类都多了一行。九、下一篇实战篇照着记事本写自己的工具前面六课是零件与配置最后一篇看一个组装好的真实工具examples\notepad—— 只有 5 个回调却把菜单分发、脏标记、状态栏刷新、文件读写与编码识别、配置持久化、关闭前拦截全做齐了。
网站建设高端定制企业官网