feat: add instance form visual boundaries
This commit is contained in:
+89
-46
@@ -41,10 +41,13 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
instanceStartIcon = mustIcon(icons.AVPlayArrow)
|
||||
instanceStopIcon = mustIcon(icons.AVStop)
|
||||
instanceEditIcon = mustIcon(icons.EditorModeEdit)
|
||||
instanceDeleteIcon = mustIcon(icons.ActionDelete)
|
||||
instanceStartIcon = mustIcon(icons.AVPlayArrow)
|
||||
instanceStopIcon = mustIcon(icons.AVStop)
|
||||
instanceEditIcon = mustIcon(icons.EditorModeEdit)
|
||||
instanceDeleteIcon = mustIcon(icons.ActionDelete)
|
||||
instanceFormWorkspaceBorder = color.NRGBA{R: 199, G: 207, B: 218, A: 255}
|
||||
instanceFormComponentBorder = color.NRGBA{R: 225, G: 230, B: 238, A: 255}
|
||||
instanceFormActionBorder = color.NRGBA{R: 215, G: 222, B: 232, A: 255}
|
||||
)
|
||||
|
||||
type PathField string
|
||||
@@ -1114,8 +1117,14 @@ func (s *Shell) editDialogCard(row InstanceRow) layout.Widget {
|
||||
return layout.Background{}.Layout(gtx,
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
rect := image.Rectangle{Max: gtx.Constraints.Min}
|
||||
defer clip.UniformRRect(rect, gtx.Dp(8)).Push(gtx.Ops).Pop()
|
||||
paint.Fill(gtx.Ops, s.theme.Palette.Bg)
|
||||
if rect.Empty() {
|
||||
return layout.Dimensions{Size: rect.Max}
|
||||
}
|
||||
paint.FillShape(gtx.Ops, instanceFormWorkspaceBorder, clip.UniformRRect(rect, gtx.Dp(10)).Op(gtx.Ops))
|
||||
inner := rect.Inset(gtx.Dp(1))
|
||||
if !inner.Empty() {
|
||||
paint.FillShape(gtx.Ops, s.theme.Palette.Bg, clip.UniformRRect(inner, gtx.Dp(9)).Op(gtx.Ops))
|
||||
}
|
||||
return layout.Dimensions{Size: gtx.Constraints.Min}
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
@@ -1126,25 +1135,29 @@ func (s *Shell) editDialogCard(row InstanceRow) layout.Widget {
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(material.Caption(s.theme, "修改下次启动时使用的保存配置。").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
||||
layout.Rigid(s.formField("实例名称", "用于在实例列表中识别此浏览器环境", &s.editName)),
|
||||
layout.Rigid(s.instanceFormField(s.formField("实例名称", "用于在实例列表中识别此浏览器环境", &s.editName))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return s.editBrowserField(gtx, locked) }),
|
||||
layout.Rigid(s.instanceFormField(func(gtx layout.Context) layout.Dimensions { return s.editBrowserField(gtx, locked) })),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return s.editDirectoryField(gtx, locked) }),
|
||||
layout.Rigid(s.instanceFormField(func(gtx layout.Context) layout.Dimensions { return s.editDirectoryField(gtx, locked) })),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(s.formField("启动 URL(可选)", "仅支持 http/https;留空时启动浏览器默认页", &s.editURL)),
|
||||
layout.Rigid(s.instanceFormField(s.formField("启动 URL(可选)", "仅支持 http/https;留空时启动浏览器默认页", &s.editURL))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return s.editPreferredPortField(gtx, locked) }),
|
||||
layout.Rigid(s.instanceFormField(func(gtx layout.Context) layout.Dimensions { return s.editPreferredPortField(gtx, locked) })),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return s.proxyPickerField(gtx, proxyPickerEdit) }),
|
||||
layout.Rigid(s.instanceFormField(func(gtx layout.Context) layout.Dimensions { return s.proxyPickerField(gtx, proxyPickerEdit) })),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(s.formField("实际调试端口", "仅在已验证的运行或外部关联状态显示;此处只读", &s.editPort)),
|
||||
layout.Rigid(s.instanceFormField(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(s.formField("实际调试端口", "仅在已验证的运行或外部关联状态显示;此处只读", &s.editPort)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(statusLabel(s.theme, row.Status)),
|
||||
)
|
||||
})),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(statusLabel(s.theme, row.Status)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(s.editFeedbackLabel(locked)),
|
||||
layout.Rigid(s.instanceFormFeedback(s.editFeedbackMessage(locked))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(16)}.Layout),
|
||||
layout.Rigid(s.editActions),
|
||||
layout.Rigid(s.instanceFormActionBar(s.editActions)),
|
||||
)
|
||||
if s.editFocusPending {
|
||||
gtx.Execute(key.FocusCmd{Tag: &s.editName})
|
||||
@@ -1208,12 +1221,12 @@ func (s *Shell) editPreferredPortField(gtx layout.Context, locked bool) layout.D
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Shell) editFeedbackLabel(locked bool) layout.Widget {
|
||||
func (s *Shell) editFeedbackMessage(locked bool) string {
|
||||
label := s.editFeedback
|
||||
if label == "" && locked {
|
||||
label = "运行中或外部关联实例的浏览器类型和 User Data Dir 已锁定。"
|
||||
}
|
||||
return material.Caption(s.theme, label).Layout
|
||||
return label
|
||||
}
|
||||
|
||||
func (s *Shell) editActions(gtx layout.Context) layout.Dimensions {
|
||||
@@ -1727,34 +1740,31 @@ func (s *Shell) createInstance(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(material.H4(s.theme, "新建实例").Layout),
|
||||
layout.Rigid(material.Body1(s.theme, "为一个独立的 Chrome 或 Edge User Data Dir 创建托管实例").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(18)}.Layout),
|
||||
layout.Rigid(s.formField("实例名称", "用于在实例列表中识别此浏览器环境", &s.instanceName)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(s.instanceDirField),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(s.formField("启动 URL(可选)", "仅支持 http/https;留空时启动浏览器默认页", &s.instanceURL)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
||||
layout.Rigid(s.formField("首选调试端口", "已按现有实例推荐;启动时会在后台复检 loopback 可用性", &s.instancePort)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return s.proxyPickerField(gtx, proxyPickerCreate) }),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
||||
layout.Rigid(material.Body2(s.theme, "浏览器类型").Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(material.RadioButton(s.theme, &s.browserKind, "chrome", "Chrome").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(material.RadioButton(s.theme, &s.browserKind, "edge", "Edge").Layout),
|
||||
layout.Rigid(s.instanceFormWorkspace(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(s.instanceFormField(s.formField("实例名称", "用于在实例列表中识别此浏览器环境", &s.instanceName))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.instanceFormField(s.instanceDirField)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.instanceFormField(s.createBrowserField)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.instanceFormField(s.formField("启动 URL(可选)", "仅支持 http/https;留空时启动浏览器默认页", &s.instanceURL))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.instanceFormField(s.formField("首选调试端口", "已按现有实例推荐;启动时会在后台复检 loopback 可用性", &s.instancePort))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.instanceFormField(func(gtx layout.Context) layout.Dimensions { return s.proxyPickerField(gtx, proxyPickerCreate) })),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.instanceFormFeedback(s.formFeedback)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
||||
layout.Rigid(s.instanceFormActionBar(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(material.Button(s.theme, &s.createClick, "创建实例").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.backClick, "取消").Layout),
|
||||
)
|
||||
})),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(material.Caption(s.theme, s.formFeedback).Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(18)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(material.Button(s.theme, &s.createClick, "创建实例").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(material.Button(s.theme, &s.backClick, "取消").Layout),
|
||||
)
|
||||
}),
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1769,6 +1779,39 @@ func (s *Shell) formField(label, help string, editor *widget.Editor) layout.Widg
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shell) createBrowserField(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.Body2(s.theme, "浏览器类型").Layout),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(material.RadioButton(s.theme, &s.browserKind, "chrome", "Chrome").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(material.RadioButton(s.theme, &s.browserKind, "edge", "Edge").Layout),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Shell) instanceFormWorkspace(content layout.Widget) layout.Widget {
|
||||
return s.settingsSurface(instanceFormWorkspaceBorder, unit.Dp(10), unit.Dp(12), content)
|
||||
}
|
||||
|
||||
func (s *Shell) instanceFormField(content layout.Widget) layout.Widget {
|
||||
return s.settingsSurface(instanceFormComponentBorder, unit.Dp(6), unit.Dp(8), content)
|
||||
}
|
||||
|
||||
func (s *Shell) instanceFormActionBar(content layout.Widget) layout.Widget {
|
||||
return s.settingsSurface(instanceFormActionBorder, unit.Dp(6), unit.Dp(8), content)
|
||||
}
|
||||
|
||||
func (s *Shell) instanceFormFeedback(message string) layout.Widget {
|
||||
if strings.TrimSpace(message) == "" {
|
||||
return func(layout.Context) layout.Dimensions { return layout.Dimensions{} }
|
||||
}
|
||||
return s.settingsSurface(instanceFormActionBorder, unit.Dp(6), unit.Dp(8), material.Caption(s.theme, message).Layout)
|
||||
}
|
||||
|
||||
func (s *Shell) proxyPickerField(gtx layout.Context, target proxyPickerTarget) layout.Dimensions {
|
||||
click := &s.createProxyPick
|
||||
selected := s.createProxyID
|
||||
|
||||
Reference in New Issue
Block a user