feat: add portable directory safeguards
This commit is contained in:
+255
-47
@@ -242,6 +242,8 @@ type directoryPickTarget uint8
|
||||
const (
|
||||
directoryPickCreate directoryPickTarget = iota
|
||||
directoryPickEdit
|
||||
directoryPickDefaultUserData
|
||||
directoryPickLogDirectory
|
||||
)
|
||||
|
||||
type proxyPickerTarget uint8
|
||||
@@ -278,7 +280,9 @@ type SettingsState struct {
|
||||
ChromePath string
|
||||
EdgePath string
|
||||
DefaultDir string
|
||||
DefaultDirMode string
|
||||
LogDir string
|
||||
LogDirMode string
|
||||
RemoteDebugStartPort int
|
||||
ReservedRemoteDebugPorts []int
|
||||
CloseOnExit bool
|
||||
@@ -304,8 +308,10 @@ type Shell struct {
|
||||
edgeSearch widget.Clickable
|
||||
dataPick widget.Clickable
|
||||
dataSearch widget.Clickable
|
||||
dataRestore widget.Clickable
|
||||
logPick widget.Clickable
|
||||
logSearch widget.Clickable
|
||||
logRestore widget.Clickable
|
||||
saveClick widget.Clickable
|
||||
cancelClick widget.Clickable
|
||||
|
||||
@@ -399,6 +405,13 @@ type Shell struct {
|
||||
nextInstance uint64
|
||||
pendingCreateID string
|
||||
defaultInstanceDirRoot string
|
||||
portableDefaultDir string
|
||||
portableLogDir string
|
||||
defaultDirMode string
|
||||
logDirMode string
|
||||
lastSavedSettings SettingsState
|
||||
defaultCreateDirActive bool
|
||||
lastSuggestedCreateDir string
|
||||
instanceFeedback string
|
||||
pendingDeleteID string
|
||||
editingID string
|
||||
@@ -455,6 +468,10 @@ func NewShell(theme *material.Theme) *Shell {
|
||||
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.portableDefaultDir = s.dataDir.Text()
|
||||
s.portableLogDir = s.logDir.Text()
|
||||
s.defaultDirMode = "portable"
|
||||
s.logDirMode = "portable"
|
||||
s.remoteDebugPort.SetText(strconv.Itoa(domain.DefaultRemoteDebugPort))
|
||||
s.instancePort.SetText(strconv.Itoa(s.recommendRemoteDebugPort("")))
|
||||
s.closeOnExit.Value = true
|
||||
@@ -464,6 +481,7 @@ func NewShell(theme *material.Theme) *Shell {
|
||||
s.list.Axis = layout.Vertical
|
||||
s.settingsList.Axis = layout.Vertical
|
||||
s.tabsList.Axis = layout.Vertical
|
||||
s.lastSavedSettings = SettingsState{ChromePath: s.chromePath.Text(), EdgePath: s.edgePath.Text(), DefaultDir: s.dataDir.Text(), DefaultDirMode: s.defaultDirMode, LogDir: s.logDir.Text(), LogDirMode: s.logDirMode, RemoteDebugStartPort: domain.DefaultRemoteDebugPort, CloseOnExit: true}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -518,17 +536,52 @@ func (s *Shell) SetDefaultInstanceUserDataRoot(root string) {
|
||||
s.defaultInstanceDirRoot = filepath.Clean(root)
|
||||
}
|
||||
|
||||
// SetPortableDirectoryDefaults supplies paths already resolved from chub.exe.
|
||||
// It deliberately accepts only strings so the Gio shell never reads the file
|
||||
// system while rendering or handling user input.
|
||||
func (s *Shell) SetPortableDirectoryDefaults(defaultDir, logDir string) {
|
||||
defaultDir = strings.TrimSpace(defaultDir)
|
||||
logDir = strings.TrimSpace(logDir)
|
||||
if defaultDir != "" && filepath.IsAbs(defaultDir) {
|
||||
s.portableDefaultDir = filepath.Clean(defaultDir)
|
||||
if s.defaultDirMode == "" || s.defaultDirMode == "portable" {
|
||||
s.defaultDirMode = "portable"
|
||||
s.dataDir.SetText(s.portableDefaultDir)
|
||||
s.SetDefaultInstanceUserDataRoot(s.portableDefaultDir)
|
||||
}
|
||||
}
|
||||
if logDir != "" && filepath.IsAbs(logDir) {
|
||||
s.portableLogDir = filepath.Clean(logDir)
|
||||
if s.logDirMode == "" || s.logDirMode == "portable" {
|
||||
s.logDirMode = "portable"
|
||||
s.logDir.SetText(s.portableLogDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shell) SetSettings(value SettingsState) {
|
||||
s.chromePath.SetText(value.ChromePath)
|
||||
s.edgePath.SetText(value.EdgePath)
|
||||
s.dataDir.SetText(value.DefaultDir)
|
||||
s.logDir.SetText(value.LogDir)
|
||||
s.defaultDirMode = normalizeDirectoryMode(value.DefaultDirMode)
|
||||
s.logDirMode = normalizeDirectoryMode(value.LogDirMode)
|
||||
if s.defaultDirMode == "portable" && s.portableDefaultDir != "" {
|
||||
s.dataDir.SetText(s.portableDefaultDir)
|
||||
}
|
||||
if s.logDirMode == "portable" && s.portableLogDir != "" {
|
||||
s.logDir.SetText(s.portableLogDir)
|
||||
}
|
||||
if root := strings.TrimSpace(s.dataDir.Text()); root != "" && filepath.IsAbs(root) {
|
||||
s.SetDefaultInstanceUserDataRoot(root)
|
||||
}
|
||||
port := value.RemoteDebugStartPort
|
||||
if port == 0 {
|
||||
port = domain.DefaultRemoteDebugPort
|
||||
}
|
||||
s.remoteDebugPort.SetText(strconv.Itoa(port))
|
||||
s.closeOnExit.Value = value.CloseOnExit
|
||||
s.lastSavedSettings = SettingsState{ChromePath: s.chromePath.Text(), EdgePath: s.edgePath.Text(), DefaultDir: s.dataDir.Text(), DefaultDirMode: s.defaultDirMode, LogDir: s.logDir.Text(), LogDirMode: s.logDirMode, RemoteDebugStartPort: port, CloseOnExit: s.closeOnExit.Value}
|
||||
}
|
||||
|
||||
// ReportStartupIssue records a safe, user-recoverable configuration message.
|
||||
@@ -620,6 +673,7 @@ func (s *Shell) Layout(gtx layout.Context) layout.Dimensions {
|
||||
s.consumeControls(gtx)
|
||||
}
|
||||
s.presentUnexpectedExitIfReady()
|
||||
s.syncDefaultCreateDirectory()
|
||||
mainLayout := func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
layout.Rigid(s.sidebar),
|
||||
@@ -730,12 +784,15 @@ func (s *Shell) beginCreate() {
|
||||
s.page = pageCreate
|
||||
s.formFeedback = ""
|
||||
s.pendingCreateID = s.allocateInstanceID()
|
||||
s.defaultCreateDirActive = true
|
||||
s.lastSuggestedCreateDir = ""
|
||||
s.instanceName.SetText("")
|
||||
s.instanceURL.SetText("")
|
||||
s.createProxyID = ""
|
||||
s.browserKind.Value = "chrome"
|
||||
if defaultDir := s.defaultCreateInstanceDir(s.pendingCreateID); defaultDir != "" {
|
||||
if defaultDir := s.defaultCreateInstanceDir("新实例", s.pendingCreateID); defaultDir != "" {
|
||||
s.instanceDir.SetText(defaultDir)
|
||||
s.lastSuggestedCreateDir = defaultDir
|
||||
} else {
|
||||
s.instanceDir.SetText("")
|
||||
s.formFeedback = "无法确定程序目录中的默认 User Data Dir;请选择或输入一个绝对路径。"
|
||||
@@ -904,9 +961,9 @@ func (s *Shell) saveEdit() {
|
||||
s.closeEdit("要编辑的实例已不存在。")
|
||||
return
|
||||
}
|
||||
name := strings.TrimSpace(s.editName.Text())
|
||||
if name == "" {
|
||||
s.editFeedback = "请输入实例名称。"
|
||||
name, err := s.validateInstanceName(s.editName.Text(), row.ID)
|
||||
if err != nil {
|
||||
s.editFeedback = err.Error()
|
||||
return
|
||||
}
|
||||
targetURL, err := normalizeTargetURL(s.editURL.Text())
|
||||
@@ -1358,7 +1415,7 @@ func (s *Shell) editDirectoryField(gtx layout.Context, locked bool) layout.Dimen
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.Body2(s.theme, "User Data Dir").Layout),
|
||||
layout.Rigid(material.Caption(s.theme, "浏览器独立数据目录;必须是绝对路径").Layout),
|
||||
layout.Rigid(material.Caption(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,
|
||||
@@ -1962,17 +2019,23 @@ func (s *Shell) settings(gtx layout.Context) layout.Dimensions {
|
||||
s.togglePathSearch(PathEdgeExecutable)
|
||||
}
|
||||
for s.dataPick.Clicked(gtx) {
|
||||
s.pathFeedback = "请选择默认 User Data Dir"
|
||||
s.chooseDirectory(directoryPickDefaultUserData)
|
||||
}
|
||||
for s.dataSearch.Clicked(gtx) {
|
||||
s.togglePathSearch(PathDefaultUserData)
|
||||
}
|
||||
for s.logPick.Clicked(gtx) {
|
||||
s.pathFeedback = "请选择日志目录"
|
||||
s.chooseDirectory(directoryPickLogDirectory)
|
||||
}
|
||||
for s.logSearch.Clicked(gtx) {
|
||||
s.togglePathSearch(PathLogDirectory)
|
||||
}
|
||||
for s.dataRestore.Clicked(gtx) {
|
||||
s.restorePortableDirectory(PathDefaultUserData)
|
||||
}
|
||||
for s.logRestore.Clicked(gtx) {
|
||||
s.restorePortableDirectory(PathLogDirectory)
|
||||
}
|
||||
for s.proxySave.Clicked(gtx) {
|
||||
s.saveProxy()
|
||||
}
|
||||
@@ -1986,6 +2049,10 @@ func (s *Shell) settings(gtx layout.Context) layout.Dimensions {
|
||||
continue
|
||||
}
|
||||
s.remoteDebugPort.SetText(strconv.Itoa(settings.RemoteDebugStartPort))
|
||||
s.defaultDirMode = settings.DefaultDirMode
|
||||
s.logDirMode = settings.LogDirMode
|
||||
s.defaultInstanceDirRoot = settings.DefaultDir
|
||||
s.lastSavedSettings = settings
|
||||
if s.onSave != nil {
|
||||
s.onSave(settings)
|
||||
}
|
||||
@@ -2018,9 +2085,9 @@ func (s *Shell) settingsWorkspace(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
layout.Rigid(s.settingsSection("默认目录", "这些目录只作为新建实例和诊断日志的默认位置,不会移动或删除已有 profile。", func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(s.pathField(PathDefaultUserData, "默认 User Data Dir", &s.dataDir, &s.dataPick, &s.dataSearch)),
|
||||
layout.Rigid(s.portableDirectoryField(PathDefaultUserData, "默认 User Data Dir", &s.dataDir, &s.dataPick, &s.dataSearch, &s.dataRestore)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(s.pathField(PathLogDirectory, "日志目录", &s.logDir, &s.logPick, &s.logSearch)),
|
||||
layout.Rigid(s.portableDirectoryField(PathLogDirectory, "日志目录", &s.logDir, &s.logPick, &s.logSearch, &s.logRestore)),
|
||||
)
|
||||
})),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
||||
@@ -2182,6 +2249,26 @@ func (s *Shell) pathField(field PathField, label string, editor *widget.Editor,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shell) portableDirectoryField(field PathField, label string, editor *widget.Editor, pick, search, restore *widget.Clickable) layout.Widget {
|
||||
return func(gtx layout.Context) layout.Dimensions {
|
||||
mode := s.directoryMode(field)
|
||||
help := "自定义目录;仅影响后续新建实例或后续日志写入。"
|
||||
if mode == "portable" {
|
||||
help = "跟随程序目录;下次启动会按 chub.exe 所在目录重新解析。"
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(s.pathField(field, label, editor, pick, search)),
|
||||
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.Flexed(1, material.Caption(s.theme, help).Layout),
|
||||
layout.Rigid(material.Button(s.theme, restore, "恢复程序目录默认").Layout),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shell) createInstance(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.H4(s.theme, "新建实例").Layout),
|
||||
@@ -2446,7 +2533,7 @@ func (s *Shell) proxyPickerNoneLabel() string {
|
||||
func (s *Shell) instanceDirField(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(material.Body2(s.theme, "User Data Dir").Layout),
|
||||
layout.Rigid(material.Caption(s.theme, "浏览器独立数据目录;必须是绝对路径").Layout),
|
||||
layout.Rigid(material.Caption(s.theme, "默认目录会使用 instance-实例名-短 ID;必须是绝对路径").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,
|
||||
@@ -2459,11 +2546,20 @@ func (s *Shell) instanceDirField(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
|
||||
func (s *Shell) createInstanceFromForm() {
|
||||
name := strings.TrimSpace(s.instanceName.Text())
|
||||
if name == "" {
|
||||
s.formFeedback = "请输入实例名称后再创建。"
|
||||
name, err := s.validateInstanceName(s.instanceName.Text(), "")
|
||||
if err != nil {
|
||||
s.formFeedback = err.Error()
|
||||
return
|
||||
}
|
||||
if s.defaultCreateDirActive {
|
||||
defaultDir := s.defaultCreateInstanceDir(name, s.pendingCreateID)
|
||||
if defaultDir == "" {
|
||||
s.formFeedback = "无法根据实例名称生成默认 User Data Dir;请选择或输入一个绝对路径。"
|
||||
return
|
||||
}
|
||||
s.instanceDir.SetText(defaultDir)
|
||||
s.lastSuggestedCreateDir = defaultDir
|
||||
}
|
||||
userDataDir := strings.TrimSpace(s.instanceDir.Text())
|
||||
if userDataDir == "" || !filepath.IsAbs(userDataDir) {
|
||||
s.formFeedback = "请选择或输入绝对路径的 User Data Dir 后再创建。"
|
||||
@@ -2495,14 +2591,66 @@ func (s *Shell) createInstanceFromForm() {
|
||||
s.page = pageInstances
|
||||
s.formFeedback = ""
|
||||
s.pendingCreateID = ""
|
||||
s.defaultCreateDirActive = false
|
||||
s.lastSuggestedCreateDir = ""
|
||||
s.notifyInstancesChanged()
|
||||
}
|
||||
|
||||
func (s *Shell) defaultCreateInstanceDir(instanceID string) string {
|
||||
func (s *Shell) defaultCreateInstanceDir(name, instanceID string) string {
|
||||
if s.defaultInstanceDirRoot == "" || instanceID == "" {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(s.defaultInstanceDirRoot, instanceID)
|
||||
component, err := domain.InstanceProfileDirectoryName(name, instanceID)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(s.defaultInstanceDirRoot, component)
|
||||
}
|
||||
|
||||
func (s *Shell) syncDefaultCreateDirectory() {
|
||||
if !s.defaultCreateDirActive || s.pendingCreateID == "" {
|
||||
return
|
||||
}
|
||||
current := strings.TrimSpace(s.instanceDir.Text())
|
||||
if s.lastSuggestedCreateDir != "" && !samePathText(current, s.lastSuggestedCreateDir) {
|
||||
s.defaultCreateDirActive = false
|
||||
return
|
||||
}
|
||||
name := strings.TrimSpace(s.instanceName.Text())
|
||||
if name == "" {
|
||||
name = "新实例"
|
||||
}
|
||||
suggested := s.defaultCreateInstanceDir(name, s.pendingCreateID)
|
||||
if suggested == "" || samePathText(suggested, s.lastSuggestedCreateDir) {
|
||||
return
|
||||
}
|
||||
s.instanceDir.SetText(suggested)
|
||||
s.lastSuggestedCreateDir = suggested
|
||||
}
|
||||
|
||||
func (s *Shell) validateInstanceName(value, excludeID string) (string, error) {
|
||||
name, err := domain.NormalizeInstanceName(value)
|
||||
if err != nil {
|
||||
return "", errors.New("实例名称不能为空,且不能包含 Windows 非法字符、保留设备名或末尾句点。")
|
||||
}
|
||||
for _, row := range s.rows {
|
||||
if row.ID == excludeID {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(strings.TrimSpace(row.Name), name) {
|
||||
return "", errors.New("实例名称已存在,请更换。")
|
||||
}
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func samePathText(left, right string) bool {
|
||||
left = strings.TrimSpace(left)
|
||||
right = strings.TrimSpace(right)
|
||||
if left == "" || right == "" {
|
||||
return left == right
|
||||
}
|
||||
return strings.EqualFold(filepath.Clean(left), filepath.Clean(right))
|
||||
}
|
||||
|
||||
func (s *Shell) allocateInstanceID() string {
|
||||
@@ -2668,6 +2816,9 @@ func (s *Shell) pathPickButtonLabel(field PathField) string {
|
||||
if s.executablePick.running && s.executablePick.field == field {
|
||||
return "选择中…"
|
||||
}
|
||||
if s.directoryPick.running && ((field == PathDefaultUserData && s.directoryPick.target == directoryPickDefaultUserData) || (field == PathLogDirectory && s.directoryPick.target == directoryPickLogDirectory)) {
|
||||
return "选择中…"
|
||||
}
|
||||
return "选择"
|
||||
}
|
||||
|
||||
@@ -3442,16 +3593,75 @@ func (s *Shell) settingsState() (SettingsState, error) {
|
||||
if err != nil {
|
||||
return SettingsState{}, err
|
||||
}
|
||||
defaultDir, err := normalizeSettingsDirectory(s.dataDir.Text(), "默认 User Data Dir")
|
||||
if err != nil {
|
||||
return SettingsState{}, err
|
||||
}
|
||||
logDir, err := normalizeSettingsDirectory(s.logDir.Text(), "日志目录")
|
||||
if err != nil {
|
||||
return SettingsState{}, err
|
||||
}
|
||||
return SettingsState{
|
||||
ChromePath: s.chromePath.Text(),
|
||||
EdgePath: s.edgePath.Text(),
|
||||
DefaultDir: s.dataDir.Text(),
|
||||
LogDir: s.logDir.Text(),
|
||||
DefaultDir: defaultDir,
|
||||
DefaultDirMode: s.selectedDirectoryMode(defaultDir, s.portableDefaultDir, s.defaultDirMode),
|
||||
LogDir: logDir,
|
||||
LogDirMode: s.selectedDirectoryMode(logDir, s.portableLogDir, s.logDirMode),
|
||||
RemoteDebugStartPort: port,
|
||||
CloseOnExit: s.closeOnExit.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeSettingsDirectory(value, label string) (string, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" || !filepath.IsAbs(value) {
|
||||
return "", fmt.Errorf("%s必须是绝对路径。", label)
|
||||
}
|
||||
return filepath.Clean(value), nil
|
||||
}
|
||||
|
||||
func normalizeDirectoryMode(value string) string {
|
||||
if strings.EqualFold(strings.TrimSpace(value), "portable") {
|
||||
return "portable"
|
||||
}
|
||||
return "custom"
|
||||
}
|
||||
|
||||
func (s *Shell) selectedDirectoryMode(value, portable, currentMode string) string {
|
||||
if strings.EqualFold(currentMode, "portable") && samePathText(value, portable) {
|
||||
return "portable"
|
||||
}
|
||||
return "custom"
|
||||
}
|
||||
|
||||
func (s *Shell) directoryMode(field PathField) string {
|
||||
if field == PathDefaultUserData {
|
||||
return s.selectedDirectoryMode(s.dataDir.Text(), s.portableDefaultDir, s.defaultDirMode)
|
||||
}
|
||||
return s.selectedDirectoryMode(s.logDir.Text(), s.portableLogDir, s.logDirMode)
|
||||
}
|
||||
|
||||
func (s *Shell) restorePortableDirectory(field PathField) {
|
||||
if field == PathDefaultUserData {
|
||||
if s.portableDefaultDir == "" {
|
||||
s.setPathFieldFeedback(field, "无法确定程序目录中的默认 User Data Dir。")
|
||||
return
|
||||
}
|
||||
s.dataDir.SetText(s.portableDefaultDir)
|
||||
s.defaultDirMode = "portable"
|
||||
s.setPathFieldFeedback(field, "已恢复为程序目录下的默认 User Data Dir。")
|
||||
return
|
||||
}
|
||||
if s.portableLogDir == "" {
|
||||
s.setPathFieldFeedback(field, "无法确定程序目录中的默认日志目录。")
|
||||
return
|
||||
}
|
||||
s.logDir.SetText(s.portableLogDir)
|
||||
s.logDirMode = "portable"
|
||||
s.setPathFieldFeedback(field, "已恢复为程序目录下的默认日志目录。")
|
||||
}
|
||||
|
||||
func normalizeRemoteDebugStartPort(value string) (int, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
@@ -3584,19 +3794,11 @@ func (s *Shell) chooseInstanceDirectory() {
|
||||
|
||||
func (s *Shell) chooseDirectory(target directoryPickTarget) {
|
||||
if s.directoryPick.running {
|
||||
if target == directoryPickEdit {
|
||||
s.editFeedback = "目录选择器已打开,请在系统窗口中选择或取消。"
|
||||
} else {
|
||||
s.formFeedback = "目录选择器已打开,请在系统窗口中选择或取消。"
|
||||
}
|
||||
s.setDirectoryPickFeedback(target, "目录选择器已打开,请在系统窗口中选择或取消。")
|
||||
return
|
||||
}
|
||||
if s.directoryChooser == nil {
|
||||
if target == directoryPickEdit {
|
||||
s.editFeedback = "目录选择器尚未准备好。"
|
||||
} else {
|
||||
s.formFeedback = "目录选择器尚未准备好。"
|
||||
}
|
||||
s.setDirectoryPickFeedback(target, "目录选择器尚未准备好。")
|
||||
return
|
||||
}
|
||||
s.directoryPick.request++
|
||||
@@ -3605,11 +3807,7 @@ func (s *Shell) chooseDirectory(target directoryPickTarget) {
|
||||
s.directoryPick.cancel = cancel
|
||||
s.directoryPick.running = true
|
||||
s.directoryPick.target = target
|
||||
if target == directoryPickEdit {
|
||||
s.editFeedback = "正在打开目录选择器…"
|
||||
} else {
|
||||
s.formFeedback = "正在打开目录选择器…"
|
||||
}
|
||||
s.setDirectoryPickFeedback(target, "正在打开目录选择器…")
|
||||
go func() {
|
||||
path, err := s.directoryChooser(ctx)
|
||||
s.directoryResults <- directoryPickResult{request: request, path: path, err: err}
|
||||
@@ -3631,22 +3829,30 @@ func (s *Shell) consumeDirectoryResults() {
|
||||
s.directoryPick.cancel = nil
|
||||
if result.err != nil {
|
||||
if errors.Is(result.err, context.Canceled) {
|
||||
s.setDirectoryPickFeedback(target, "已取消选择 User Data Dir。")
|
||||
s.setDirectoryPickFeedback(target, "已取消选择目录。")
|
||||
} else {
|
||||
s.setDirectoryPickFeedback(target, fmt.Sprintf("选择 User Data Dir 失败:%v", result.err))
|
||||
s.setDirectoryPickFeedback(target, fmt.Sprintf("选择目录失败:%v", result.err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if result.path == "" {
|
||||
s.setDirectoryPickFeedback(target, "未选择 User Data Dir。")
|
||||
s.setDirectoryPickFeedback(target, "未选择目录。")
|
||||
continue
|
||||
}
|
||||
if target == directoryPickEdit {
|
||||
switch target {
|
||||
case directoryPickEdit:
|
||||
s.editDir.SetText(result.path)
|
||||
} else {
|
||||
case directoryPickDefaultUserData:
|
||||
s.dataDir.SetText(result.path)
|
||||
s.defaultDirMode = "custom"
|
||||
case directoryPickLogDirectory:
|
||||
s.logDir.SetText(result.path)
|
||||
s.logDirMode = "custom"
|
||||
default:
|
||||
s.instanceDir.SetText(result.path)
|
||||
s.defaultCreateDirActive = false
|
||||
}
|
||||
s.setDirectoryPickFeedback(target, "已更新 User Data Dir。")
|
||||
s.setDirectoryPickFeedback(target, "已更新目录。")
|
||||
default:
|
||||
return
|
||||
}
|
||||
@@ -3654,24 +3860,26 @@ func (s *Shell) consumeDirectoryResults() {
|
||||
}
|
||||
|
||||
func (s *Shell) setDirectoryPickFeedback(target directoryPickTarget, message string) {
|
||||
if target == directoryPickEdit {
|
||||
switch target {
|
||||
case directoryPickEdit:
|
||||
s.editFeedback = message
|
||||
return
|
||||
case directoryPickDefaultUserData:
|
||||
s.setPathFieldFeedback(PathDefaultUserData, message)
|
||||
case directoryPickLogDirectory:
|
||||
s.setPathFieldFeedback(PathLogDirectory, message)
|
||||
default:
|
||||
s.formFeedback = message
|
||||
}
|
||||
s.formFeedback = message
|
||||
}
|
||||
|
||||
func (s *Shell) instanceDirButtonLabel() string {
|
||||
if s.directoryPick.running {
|
||||
if s.directoryPick.running && s.directoryPick.target == directoryPickCreate {
|
||||
return "选择中…"
|
||||
}
|
||||
return "选择路径"
|
||||
}
|
||||
|
||||
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`)
|
||||
s.remoteDebugPort.SetText(strconv.Itoa(domain.DefaultRemoteDebugPort))
|
||||
s.SetSettings(s.lastSavedSettings)
|
||||
s.pathFeedback = "已恢复未保存前的设置。"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user