# xui
**Repository Path**: twgh/xui
## Basic Information
- **Project Name**: xui
- **Description**: shadcn 风格的 xcgui 自绘组件, 快速开发美观的 Windows 桌面应用
- **Primary Language**: Go
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-10-19
- **Last Updated**: 2026-10-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: shadcn-ui, xcgui, Go语言, Windows
## README
# xui — shadcn 风格的 xcgui 自绘组件
## 介绍
为 [github.com/twgh/xcgui](https://github.com/twgh/xcgui)(炫彩界面库)提供一套 **shadcn-ui 风格**的自绘组件:主题令牌、变体系统、copy-not-install 分发。
```
┌─────────────────────────────────────────────┐
│ 你的应用 │
│ ┌───────────────────────────────────────┐ │
│ │ ui/ ← 组件源码归你所有, 随意修改 │ │
│ │ button.go checkbox.go input.go … │ │
│ │ theme.go ← 你的主题, 改这里换肤 │ │
│ └───────────────────────────────────────┘ │
│ ↑ 依赖 ↑ 拷贝 │
│ github.com/twgh/xui (运行时) xui CLI │
│ ↑ │
│ github.com/twgh/xcgui (行为层) │
└─────────────────────────────────────────────┘
```
### 示例图

## 快速开始
### 直接运行示例(想先查看效果)
不想先建项目时, 可以直接运行仓库里的 demo(全部组件 + 亮/暗主题切换):
```bash
go run github.com/twgh/xui/demo@latest
```
### 新建项目(推荐)
```bash
go run github.com/twgh/xui/cmd/xui@latest init -n myapp
cd myapp
go run .
```
`-n` 会创建一个完整项目目录:`go.mod` + `main.go`(AppWindow + 示例卡片) + `README.md` +
`.gitignore` + `ui/`(theme.go + 基础组件 + DESIGN.md), 并**自动执行 `go mod tidy`** 装好
依赖(生成 `go.sum`)。目录已存在且非空时需加 `-f/--force`; 想跳过依赖安装(如离线)加
`--no-install`, 之后手动跑一次 `go mod tidy`。
### 已有项目
```bash
# 先在你的项目根目录执行(需要 Go 1.18+;项目要先有 go.mod,没有就先 go mod init)
go run github.com/twgh/xui/cmd/xui@latest init
```
此时 `init` 只创建 `ui/` 目录:写入 `theme.go`(你的主题文件)、拷入全部基础组件,外加一份
`DESIGN.md` —— 组件源码的注释里到处是"见 DESIGN 第 N 节",这份**自定义指南**随源码一起安装在你的项目里:
令牌表、组件解剖、xcgui 踩坑全集、已验证 API 清单都在里面。改组件前先翻它。
```go
package main
import (
"github.com/twgh/xcgui/app"
ui "yourmodule/ui" // 换成你的模块路径
)
func main() {
app.InitOrExit()
a := app.New(true) // Direct2D 渲染
a.EnableAutoDPI(true).EnableDPI(true) // 跟随系统 DPI
a.SetPaintFrequency(10) // hover 过渡 ~60fps
// 窗口: 圆角阴影 + 自绘标题栏(最小化/最大化/关闭) + 主题化底色, 全部默认配好
w := ui.NewAppWindow("我的应用", 640, 480)
content := w.Content()
content.SetPadding(24, 20, 24, 24) // 内容区留白
ui.NewButton(0, 0, 0, 0, "确定", content.Handle)
ui.NewCheckbox(0, 0, 0, 0, "记住我", content.Handle)
ui.NewInput(0, 0, 0, 0, content.Handle, ui.WithPlaceholder("用户名"))
w.Show(true)
a.Run()
a.Exit()
}
```
标题栏要加自己的按钮(如切换主题)用 `w.AddCaptionButton(icons.IconMoon, func() { ... })`,
排在最小化按钮左边; 内容组件纵向排列, 间距由内容容器的 `SetSpace` 控制。
## 换肤
组件在**绘制时**实时读取主题令牌(等价于 CSS 变量),切换主题零成本:
```go
xui.SetTheme(xui.Dark()) // 一行全局换肤
```
主题文件 `ui/theme.go` 默认是**内置主题的副本**(逐字段改即可换肤),也可以照 shadcn 的
`:root` / `.dark` 自己写一份 —— 令牌与 CSS 变量一一对应(字段表见 DESIGN.md 3.1)。
## 组件
| 组件 | 说明 |
|---|---|
| Button | 6 变体(Primary/Secondary/Outline/Ghost/Destructive/Link)× 8 尺寸档(xs/sm/default/lg + 4 个方形图标档),hover/按下/禁用/焦点环/校验失败(`WithButtonInvalid`);`WithIcon` 做图标按钮,`WithButtonLeadingIcon` / `WithButtonTrailingIcon` 做"图标+文字",`WithButtonAlign(Start/Between)` 控制整宽按钮的内容对齐(示例里的 justify-start/justify-between)、`WithButtonRounded` 胶囊、`WithButtonExpanded` / `WithButtonHasPopup` 对应弹层触发区、`WithButtonLoading` 显示 1s/圈旋转的加载图标;`cx` 传 0 时按文字自动量宽 |
| ButtonGroup | 按钮组(`components/buttongroup.go`):横向/纵向(`WithButtonGroupOrientation`)、首尾外角 + 相邻边直角、宽高按内容自撑;可放 Button / Input / ComboBox;`NewButtonGroupText` 文本块、`NewButtonGroupSeparator` 分隔条跟随组高;组里嵌组自动 8px 间距 |
| Alert | 提示条:图标 + 标题 + 描述 + 右上动作槽;`AlertDefault` / `AlertDestructive` 两变体,标题/描述随宽度自动折行、高度按内容自撑;`Action()` 里放 Button / Badge |
| Empty | 空状态容器(`components/empty.go`):图标底板(`EmptyMediaIcon`, 40px bg-muted 圆角)/无底板媒体 + 标题 + 描述 + 动作区;`NewEmpty` 的 `Header()` / `Content()` / `Body()` 三个槽位,内边距 24、间距 24、整块居中;不画边框(与上游默认一致),高度自撑,整页空态可 `xui.ItemFillHeight` 让内容垂直居中 |
| Field | 表单字段布局(`components/field.go`):`NewField` + `Label` / `Title` / `Description` / `Content` / `SetInvalid`;`FieldSet`(gap 24)/ `FieldGroup`(gap 28)/ `FieldLegend` / `FieldSeparator`(可带居中文字)/ `FieldError`(多条去重);垂直/水平取向,校验失败时标签转 destructive;横向 `Content` 默认 240 宽(嵌套布局只认 Fixed 宽,用 `xui.ItemWidth` 覆盖) |
| Item | 列表项(`components/item.go`):`NewItem` + `Media()` / `Content()` / `Actions()` / `Header()` / `Footer()` 槽位;`ItemDefault` / `ItemOutline` / `ItemMuted` 三变体,`ItemSizeDefault` / `ItemSizeSm` 两档;`ItemMedia`(icon 变体 32 底板)/ `NewItemTitle` / `NewItemDescription` / `NewItemGroup` / `NewItemSeparator`;Content 宽度按媒体/动作实测自动锁成 Fixed |
| Breadcrumb | 面包屑(`components/breadcrumb.go`):`NewBreadcrumb` 横向换行列表 + `NewBreadcrumbItem` / `NewBreadcrumbLink`(hover 过渡到前景色、`OnClick`)/ `NewBreadcrumbPage` / `NewBreadcrumbSeparator`(可换图标)/ `NewBreadcrumbEllipsis`;文本自绘、按字体精确量宽 |
| Kbd | 键帽(`components/kbd.go`):高 20、bg-muted、rounded-sm、text-xs,宽度按文字自适应;`NewKbdGroup` 组合键间距 4 |
| Skeleton | 骨架屏(`components/skeleton.go`):bg-accent + 主题圆角 + 2s 脉冲;`WithSkeletonCircle` 头像、`WithSkeletonRadius` 自定义圆角、`WithSkeletonStatic` 关闭动画 |
| Spinner | 加载指示器(`components/spinner.go`):Loader2 图标 1s/圈旋转,`WithSpinnerSize` / `WithSpinnerMuted` / `WithSpinnerColorFunc`,`SetSpinning` 开关 |
| Toggle | 双状态按钮(`components/toggle.go`):`ToggleVariantDefault`(透明底)/ `ToggleVariantOutline`(border-input)、`ToggleSizeDefault/Sm/Lg`(h-9/h-8/h-10);`WithTogglePressed` 初始选中、`WithToggleIcon` 行内图标、`WithToggleInvalid` 校验失败;鼠标/空格/回车切换,支持焦点环与 `OnChange` |
| ToggleGroup | 切换按钮组(`components/toggle.go`):`NewToggleGroup(parent, WithToggleGroupVariant/Size/Spacing/Multiple)` + `AddItem(value, text, ...)`;单选默认同组互斥且可全部取消,`Value` / `SetValue` / `OnChange`,spacing=0 首尾外角(分段控件),spacing 为逻辑像素 |
| NativeSelect | 原生下拉框(`components/native_select.go`):ComboBox 载体 + `