Golang创建无边框窗体程序
1.主要核心库是 https://github.com/energye/energy
2.参考官网安装完energy开发环境后,编写golang代码
package main
import (
"syscall"
"github.com/energye/energy/v2/cef"
"github.com/energye/energy/v2/cef/ipc"
"github.com/energye/energy/v2/cef/ipc/context"
"github.com/lxn/win"
)
var (
orainWndProc uintptr
)
func main() {
cef.GlobalInit(nil, nil)
cefApp := cef.NewApplication()
//本地网页路径
cef.BrowserWindow.Config.Url = "http://localhost:3000/"
cef.BrowserWindow.Config.EnableHideCaption = true
cef.BrowserWindow.Config.Width = 1200
cef.BrowserWindow.Config.Height = 800
//窗体拖拽
ipc.On("window-drag", func(context context.IContext) {
win.ReleaseCapture()
win.PostMessage(win.HWND(cef.BrowserWindow.MainWindow().Handle()), 274, 0xF010+0x0002, 0)
})
//窗体最小化、最大化、还原
ipc.On("window-state", func(context context.IContext) {
bw := cef.BrowserWindow.GetWindowInfo(context.BrowserId())
state := context.ArgumentList().GetIntByIndex(0)
if state == 0 {
bw.Minimize()
} else if state == 1 {
bw.Maximize()
} else if state == 2 {
bw.Restore()
}
})
//窗体关闭
ipc.On("window-close", func(context context.IContext) {
bw := cef.BrowserWindow.GetWindowInfo(context.BrowserId())
bw.CloseBrowserWindow()
})
cef.BrowserWindow.SetBrowserInit(func(event *cef.BrowserEvent, window cef.IBrowserWindow) {
defaultWndProcPtr := syscall.NewCallback(defaultWndProc)
orainWndProc = win.SetWindowLongPtr(win.HWND(cef.BrowserWindow.MainWindow().Handle()), win.GWLP_WNDPROC, defaultWndProcPtr)
})
//Cef进入消息循环
cef.Run(cefApp)
}
func defaultWndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintptr) {
switch msg {
case win.WM_SIZE:
if wParam == 0 || wParam == 2 {
ipc.Emit("window-resize", wParam)
}
}
return win.CallWindowProc(orainWndProc, hwnd, msg, wParam, lParam)
}
package main
import (
"syscall"
"github.com/energye/energy/v2/cef"
"github.com/energye/energy/v2/cef/ipc"
"github.com/energye/energy/v2/cef/ipc/context"
"github.com/lxn/win"
)
var (
orainWndProc uintptr
)
func main() {
cef.GlobalInit(nil, nil)
cefApp := cef.NewApplication()
//本地网页路径
cef.BrowserWindow.Config.Url = "http://localhost:3000/"
cef.BrowserWindow.Config.EnableHideCaption = true
cef.BrowserWindow.Config.Width = 1200
cef.BrowserWindow.Config.Height = 800
//窗体拖拽
ipc.On("window-drag", func(context context.IContext) {
win.ReleaseCapture()
win.PostMessage(win.HWND(cef.BrowserWindow.MainWindow().Handle()), 274, 0xF010+0x0002, 0)
})
//窗体最小化、最大化、还原
ipc.On("window-state", func(context context.IContext) {
bw := cef.BrowserWindow.GetWindowInfo(context.BrowserId())
state := context.ArgumentList().GetIntByIndex(0)
if state == 0 {
bw.Minimize()
} else if state == 1 {
bw.Maximize()
} else if state == 2 {
bw.Restore()
}
})
//窗体关闭
ipc.On("window-close", func(context context.IContext) {
bw := cef.BrowserWindow.GetWindowInfo(context.BrowserId())
bw.CloseBrowserWindow()
})
cef.BrowserWindow.SetBrowserInit(func(event *cef.BrowserEvent, window cef.IBrowserWindow) {
defaultWndProcPtr := syscall.NewCallback(defaultWndProc)
orainWndProc = win.SetWindowLongPtr(win.HWND(cef.BrowserWindow.MainWindow().Handle()), win.GWLP_WNDPROC, defaultWndProcPtr)
})
//Cef进入消息循环
cef.Run(cefApp)
}
func defaultWndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintptr) {
switch msg {
case win.WM_SIZE:
if wParam == 0 || wParam == 2 {
ipc.Emit("window-resize", wParam)
}
}
return win.CallWindowProc(orainWndProc, hwnd, msg, wParam, lParam)
}
package main import ( "syscall" "github.com/energye/energy/v2/cef" "github.com/energye/energy/v2/cef/ipc" "github.com/energye/energy/v2/cef/ipc/context" "github.com/lxn/win" ) var ( orainWndProc uintptr ) func main() { cef.GlobalInit(nil, nil) cefApp := cef.NewApplication() //本地网页路径 cef.BrowserWindow.Config.Url = "http://localhost:3000/" cef.BrowserWindow.Config.EnableHideCaption = true cef.BrowserWindow.Config.Width = 1200 cef.BrowserWindow.Config.Height = 800 //窗体拖拽 ipc.On("window-drag", func(context context.IContext) { win.ReleaseCapture() win.PostMessage(win.HWND(cef.BrowserWindow.MainWindow().Handle()), 274, 0xF010+0x0002, 0) }) //窗体最小化、最大化、还原 ipc.On("window-state", func(context context.IContext) { bw := cef.BrowserWindow.GetWindowInfo(context.BrowserId()) state := context.ArgumentList().GetIntByIndex(0) if state == 0 { bw.Minimize() } else if state == 1 { bw.Maximize() } else if state == 2 { bw.Restore() } }) //窗体关闭 ipc.On("window-close", func(context context.IContext) { bw := cef.BrowserWindow.GetWindowInfo(context.BrowserId()) bw.CloseBrowserWindow() }) cef.BrowserWindow.SetBrowserInit(func(event *cef.BrowserEvent, window cef.IBrowserWindow) { defaultWndProcPtr := syscall.NewCallback(defaultWndProc) orainWndProc = win.SetWindowLongPtr(win.HWND(cef.BrowserWindow.MainWindow().Handle()), win.GWLP_WNDPROC, defaultWndProcPtr) }) //Cef进入消息循环 cef.Run(cefApp) } func defaultWndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintptr) { switch msg { case win.WM_SIZE: if wParam == 0 || wParam == 2 { ipc.Emit("window-resize", wParam) } } return win.CallWindowProc(orainWndProc, hwnd, msg, wParam, lParam) }
3.编写网页代码,这里代码用到React + Ant Design
import React, { useState, useRef, useEffect } from "react";
import { Layout, Menu, theme, Space, Button } from "antd";
import { UploadOutlined, UserOutlined, VideoCameraOutlined, MinusOutlined, BorderOutlined, CloseOutlined, SwitcherOutlined } from "@ant-design/icons";
const { Header, Footer, Sider, Content } = Layout;
const App: React.FC = () => {
const {
token: { colorBgContainer },
} = theme.useToken();
const headRef = useRef<any>();
const [windowIsMax, setWindowIsMax] = useState<boolean>();
const [menuSelect, setMenuSelect] = useState<string[]>(["2"]);
const windowClose = () => {
(window as any)?.ipc?.emit("window-close");
};
const windowMin = () => {
(window as any)?.ipc?.emit("window-state", [0]);
};
const windowMaxOrReset = () => {
(window as any)?.ipc?.emit("window-state", [windowIsMax ? 2 : 1]);
};
const windowDrag = (e: React.MouseEvent<any>) => {
if (e.target == headRef.current) {
(window as any)?.ipc?.emit("window-drag", [windowIsMax ? 2 : 1]);
}
};
useEffect(() => {
(window as any)?.ipc?.on("window-resize", function (status: any) {
setWindowIsMax(status == 2);
});
}, []);
return (
<Layout style={{ width: "100vw", height: "100vh" }}>
<Sider>
<Menu
theme="dark"
mode="inline"
selectedKeys={menuSelect}
onSelect={(e) => setMenuSelect(e.selectedKeys)}
items={[UserOutlined, VideoCameraOutlined, UploadOutlined, UserOutlined].map((icon, index) => ({
key: String(index + 1),
icon: React.createElement(icon),
label: `nav ${index + 1}`,
}))}
/>
</Sider>
<Layout>
<Header ref={headRef} onMouseDown={windowDrag} style={{ padding: 0, background: colorBgContainer, height: "40px" }}>
<Space size={0} style={{ float: "right" }}>
<Button onClick={windowMin} style={{ verticalAlign: "top" }} type="text" icon={<MinusOutlined />}></Button>
<Button onClick={windowMaxOrReset} style={{ verticalAlign: "top" }} type="text" icon={windowIsMax ? <SwitcherOutlined /> : <BorderOutlined />}></Button>
<Button onClick={windowClose} style={{ verticalAlign: "top", color: "#000000e0" }} type="text" icon={<CloseOutlined />} danger></Button>
</Space>
</Header>
<Content style={{ margin: "24px 16px 0" }}>
<div style={{ padding: 24, minHeight: 360, background: colorBgContainer }}>content</div>
</Content>
<Footer style={{ textAlign: "center" }}>Ant Design ©2023 Created by Ant UED</Footer>
</Layout>
</Layout>
);
};
export default App;
import React, { useState, useRef, useEffect } from "react";
import { Layout, Menu, theme, Space, Button } from "antd";
import { UploadOutlined, UserOutlined, VideoCameraOutlined, MinusOutlined, BorderOutlined, CloseOutlined, SwitcherOutlined } from "@ant-design/icons";
const { Header, Footer, Sider, Content } = Layout;
const App: React.FC = () => {
const {
token: { colorBgContainer },
} = theme.useToken();
const headRef = useRef<any>();
const [windowIsMax, setWindowIsMax] = useState<boolean>();
const [menuSelect, setMenuSelect] = useState<string[]>(["2"]);
const windowClose = () => {
(window as any)?.ipc?.emit("window-close");
};
const windowMin = () => {
(window as any)?.ipc?.emit("window-state", [0]);
};
const windowMaxOrReset = () => {
(window as any)?.ipc?.emit("window-state", [windowIsMax ? 2 : 1]);
};
const windowDrag = (e: React.MouseEvent<any>) => {
if (e.target == headRef.current) {
(window as any)?.ipc?.emit("window-drag", [windowIsMax ? 2 : 1]);
}
};
useEffect(() => {
(window as any)?.ipc?.on("window-resize", function (status: any) {
setWindowIsMax(status == 2);
});
}, []);
return (
<Layout style={{ width: "100vw", height: "100vh" }}>
<Sider>
<Menu
theme="dark"
mode="inline"
selectedKeys={menuSelect}
onSelect={(e) => setMenuSelect(e.selectedKeys)}
items={[UserOutlined, VideoCameraOutlined, UploadOutlined, UserOutlined].map((icon, index) => ({
key: String(index + 1),
icon: React.createElement(icon),
label: `nav ${index + 1}`,
}))}
/>
</Sider>
<Layout>
<Header ref={headRef} onMouseDown={windowDrag} style={{ padding: 0, background: colorBgContainer, height: "40px" }}>
<Space size={0} style={{ float: "right" }}>
<Button onClick={windowMin} style={{ verticalAlign: "top" }} type="text" icon={<MinusOutlined />}></Button>
<Button onClick={windowMaxOrReset} style={{ verticalAlign: "top" }} type="text" icon={windowIsMax ? <SwitcherOutlined /> : <BorderOutlined />}></Button>
<Button onClick={windowClose} style={{ verticalAlign: "top", color: "#000000e0" }} type="text" icon={<CloseOutlined />} danger></Button>
</Space>
</Header>
<Content style={{ margin: "24px 16px 0" }}>
<div style={{ padding: 24, minHeight: 360, background: colorBgContainer }}>content</div>
</Content>
<Footer style={{ textAlign: "center" }}>Ant Design ©2023 Created by Ant UED</Footer>
</Layout>
</Layout>
);
};
export default App;
import React, { useState, useRef, useEffect } from "react"; import { Layout, Menu, theme, Space, Button } from "antd"; import { UploadOutlined, UserOutlined, VideoCameraOutlined, MinusOutlined, BorderOutlined, CloseOutlined, SwitcherOutlined } from "@ant-design/icons"; const { Header, Footer, Sider, Content } = Layout; const App: React.FC = () => { const { token: { colorBgContainer }, } = theme.useToken(); const headRef = useRef<any>(); const [windowIsMax, setWindowIsMax] = useState<boolean>(); const [menuSelect, setMenuSelect] = useState<string[]>(["2"]); const windowClose = () => { (window as any)?.ipc?.emit("window-close"); }; const windowMin = () => { (window as any)?.ipc?.emit("window-state", [0]); }; const windowMaxOrReset = () => { (window as any)?.ipc?.emit("window-state", [windowIsMax ? 2 : 1]); }; const windowDrag = (e: React.MouseEvent<any>) => { if (e.target == headRef.current) { (window as any)?.ipc?.emit("window-drag", [windowIsMax ? 2 : 1]); } }; useEffect(() => { (window as any)?.ipc?.on("window-resize", function (status: any) { setWindowIsMax(status == 2); }); }, []); return ( <Layout style={{ width: "100vw", height: "100vh" }}> <Sider> <Menu theme="dark" mode="inline" selectedKeys={menuSelect} onSelect={(e) => setMenuSelect(e.selectedKeys)} items={[UserOutlined, VideoCameraOutlined, UploadOutlined, UserOutlined].map((icon, index) => ({ key: String(index + 1), icon: React.createElement(icon), label: `nav ${index + 1}`, }))} /> </Sider> <Layout> <Header ref={headRef} onMouseDown={windowDrag} style={{ padding: 0, background: colorBgContainer, height: "40px" }}> <Space size={0} style={{ float: "right" }}> <Button onClick={windowMin} style={{ verticalAlign: "top" }} type="text" icon={<MinusOutlined />}></Button> <Button onClick={windowMaxOrReset} style={{ verticalAlign: "top" }} type="text" icon={windowIsMax ? <SwitcherOutlined /> : <BorderOutlined />}></Button> <Button onClick={windowClose} style={{ verticalAlign: "top", color: "#000000e0" }} type="text" icon={<CloseOutlined />} danger></Button> </Space> </Header> <Content style={{ margin: "24px 16px 0" }}> <div style={{ padding: 24, minHeight: 360, background: colorBgContainer }}>content</div> </Content> <Footer style={{ textAlign: "center" }}>Ant Design ©2023 Created by Ant UED</Footer> </Layout> </Layout> ); }; export default App;
Golang无边框窗体已经实现,下面截图看看效果:
