feat: add gio instances and settings shell
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
)
|
||||
|
||||
type page uint8
|
||||
|
||||
const (
|
||||
pageInstances page = iota
|
||||
pageSettings
|
||||
)
|
||||
|
||||
// InstanceRow is the read-only view model used by the first Gio shell.
|
||||
// Runtime data will replace these fixtures when the application service is wired.
|
||||
type InstanceRow struct {
|
||||
Name string
|
||||
Browser string
|
||||
Profile string
|
||||
Status string
|
||||
}
|
||||
|
||||
// Shell owns every interactive Gio widget. Keeping this state outside Layout
|
||||
// prevents focus, editor content, and list scroll position from resetting.
|
||||
type Shell struct {
|
||||
theme *material.Theme
|
||||
page page
|
||||
|
||||
instancesClick widget.Clickable
|
||||
settingsClick widget.Clickable
|
||||
newClick widget.Clickable
|
||||
startClick widget.Clickable
|
||||
saveClick widget.Clickable
|
||||
cancelClick widget.Clickable
|
||||
|
||||
chromePath widget.Editor
|
||||
edgePath widget.Editor
|
||||
dataDir widget.Editor
|
||||
logDir widget.Editor
|
||||
closeOnExit widget.Bool
|
||||
|
||||
list widget.List
|
||||
rows []InstanceRow
|
||||
}
|
||||
|
||||
func NewShell(theme *material.Theme) *Shell {
|
||||
s := &Shell{theme: theme, rows: []InstanceRow{
|
||||
{Name: "运营主账号", Browser: "Chrome", Profile: "Default", Status: "运行中"},
|
||||
{Name: "广告投放", Browser: "Edge", Profile: "Profile 2", Status: "启动中"},
|
||||
{Name: "素材采集", Browser: "Chrome", Profile: "Default", Status: "外部占用"},
|
||||
{Name: "备用环境", Browser: "Edge", Profile: "Profile 1", Status: "已退出"},
|
||||
}}
|
||||
s.chromePath.SetText(`C:\Program Files\Google\Chrome\Application\chrome.exe`)
|
||||
s.edgePath.SetText(`C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`)
|
||||
s.dataDir.SetText(`C:\Users\Public\chub\profiles`)
|
||||
s.logDir.SetText(`C:\Users\Public\chub\logs`)
|
||||
s.closeOnExit.Value = true
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Shell) Layout(gtx layout.Context) layout.Dimensions {
|
||||
for s.instancesClick.Clicked(gtx) {
|
||||
s.page = pageInstances
|
||||
}
|
||||
for s.settingsClick.Clicked(gtx) {
|
||||
s.page = pageSettings
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(s.header),
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Inset{Top: unit.Dp(20), Bottom: unit.Dp(20), Left: unit.Dp(28), Right: unit.Dp(28)}.Layout(gtx, s.content)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Shell) header(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Inset{Top: unit.Dp(18), Bottom: unit.Dp(10), Left: unit.Dp(28), Right: unit.Dp(28)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Flexed(1, material.H5(s.theme, "Chub 浏览器管理").Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.instancesClick, "实例").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.settingsClick, "设置").Layout),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Shell) content(gtx layout.Context) layout.Dimensions {
|
||||
if s.page == pageSettings {
|
||||
return s.settings(gtx)
|
||||
}
|
||||
return s.instances(gtx)
|
||||
}
|
||||
|
||||
func (s *Shell) instances(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.H4(s.theme, "实例").Layout),
|
||||
layout.Rigid(material.Body1(s.theme, "统一查看、启动和管理 Chrome / Edge 浏览器进程").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(18)}.Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.newClick, "新建实例").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(16)}.Layout),
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return material.List(s.theme, &s.list).Layout(gtx, len(s.rows), func(gtx layout.Context, i int) layout.Dimensions {
|
||||
row := s.rows[i]
|
||||
return layout.Inset{Bottom: unit.Dp(10)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.Subtitle1(s.theme, row.Name).Layout),
|
||||
layout.Rigid(material.Caption(s.theme, row.Browser+" · "+row.Profile).Layout),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(statusLabel(s.theme, row.Status)),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(16)}.Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.startClick, "启动").Layout),
|
||||
)
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func statusLabel(theme *material.Theme, status string) layout.Widget {
|
||||
style := material.Label(theme, unit.Sp(13), status)
|
||||
style.Color = map[string]color.NRGBA{
|
||||
"运行中": {R: 24, G: 125, B: 78, A: 255},
|
||||
"启动中": {R: 175, G: 105, B: 0, A: 255},
|
||||
"外部占用": {R: 190, G: 75, B: 35, A: 255},
|
||||
"已退出": {R: 100, G: 105, B: 115, A: 255},
|
||||
}[status]
|
||||
return style.Layout
|
||||
}
|
||||
|
||||
func (s *Shell) settings(gtx layout.Context) layout.Dimensions {
|
||||
for s.saveClick.Clicked(gtx) { /* persistence is T-202; retain edited values for now */
|
||||
}
|
||||
for s.cancelClick.Clicked(gtx) {
|
||||
s.resetSettings()
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.H4(s.theme, "设置").Layout),
|
||||
layout.Rigid(material.Body1(s.theme, "配置浏览器可执行文件、默认目录和本地运行行为").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(18)}.Layout),
|
||||
layout.Rigid(material.Editor(s.theme, &s.chromePath, "Chrome 可执行文件").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(material.Editor(s.theme, &s.edgePath, "Edge 可执行文件").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(material.Editor(s.theme, &s.dataDir, "默认 User Data Dir").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(material.Editor(s.theme, &s.logDir, "日志目录").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(material.CheckBox(s.theme, &s.closeOnExit, "退出时关闭托管浏览器").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(16)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(material.Button(s.theme, &s.saveClick, "保存设置").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.cancelClick, "取消").Layout),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Shell) resetSettings() {
|
||||
s.chromePath.SetText(`C:\Program Files\Google\Chrome\Application\chrome.exe`)
|
||||
s.edgePath.SetText(`C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`)
|
||||
s.dataDir.SetText(`C:\Users\Public\chub\profiles`)
|
||||
s.logDir.SetText(`C:\Users\Public\chub\logs`)
|
||||
}
|
||||
Reference in New Issue
Block a user