更新代码
This commit is contained in:
1 parent
eda7e8bc96
commit
6ff632e0b0
19 files changed
+2033
-197
No files matched your search
+111
-27
@@ -30,6 +30,7 @@ type settingsWindow struct {
|
||||
|
||||
// 模型接入:左边是接入点列表,右边是当前接入点的接口详情。
|
||||
profileList *widget.List
|
||||
emptyHint *widget.Label
|
||||
selIndex int
|
||||
loading bool // 回填表单时抑制回写
|
||||
|
||||
@@ -53,6 +54,7 @@ type settingsWindow struct {
|
||||
maxEdge *widget.Entry
|
||||
quality *widget.Entry
|
||||
diff *widget.Entry
|
||||
sens *widget.Entry
|
||||
cooldown *widget.Entry
|
||||
stable *widget.Entry
|
||||
turns *widget.Entry
|
||||
@@ -72,6 +74,9 @@ func newSettingsWindow(app *App) *settingsWindow {
|
||||
s.cfg = app.eng.Config().Clone()
|
||||
s.win = app.fyne.NewWindow(settingsTitle)
|
||||
s.win.Resize(fyne.NewSize(900, 700))
|
||||
// 点关闭只是隐藏窗口:Fyne 的窗口一旦 Close 就被销毁,
|
||||
// 工具栏的设置按钮会再也打不开它。
|
||||
s.win.SetCloseIntercept(func() { s.win.Hide() })
|
||||
s.tabs = container.NewAppTabs(
|
||||
container.NewTabItem("模型", s.buildModelTab()),
|
||||
container.NewTabItem("提示词", s.buildTemplateTab()),
|
||||
@@ -103,16 +108,10 @@ func (s *settingsWindow) reload() {
|
||||
s.cfg = s.app.eng.Config().Clone()
|
||||
|
||||
s.loading = true
|
||||
s.profileList.Refresh()
|
||||
idx := s.cfg.ProfileIndex(s.cfg.Active)
|
||||
if idx < 0 {
|
||||
idx = 0
|
||||
}
|
||||
s.selIndex = idx
|
||||
s.profileList.Select(idx)
|
||||
// 选中的项没变化时 List 不会再回调,这里显式回填一次表单。
|
||||
s.loadProfileToForm()
|
||||
s.selIndex = -1
|
||||
s.syncSelection()
|
||||
s.loading = false
|
||||
s.refreshList()
|
||||
|
||||
keys := make([]string, 0, len(s.cfg.Templates))
|
||||
for _, t := range s.cfg.Templates {
|
||||
@@ -127,6 +126,7 @@ func (s *settingsWindow) reload() {
|
||||
s.maxEdge.SetText(strconv.Itoa(c.MaxEdge))
|
||||
s.quality.SetText(strconv.Itoa(c.JPEGQuality))
|
||||
s.diff.SetText(strconv.Itoa(c.DiffThreshold))
|
||||
s.sens.SetText(fmt.Sprintf("%g", c.DiffSensitivity))
|
||||
s.cooldown.SetText(strconv.Itoa(c.CooldownMs))
|
||||
s.stable.SetText(strconv.Itoa(c.StableFrames))
|
||||
s.turns.SetText(strconv.Itoa(c.HistoryTurns))
|
||||
@@ -202,12 +202,16 @@ func (s *settingsWindow) buildProfilePane() fyne.CanvasObject {
|
||||
)
|
||||
s.profileList.OnSelected = func(id widget.ListItemID) { s.selectProfile(int(id)) }
|
||||
|
||||
s.emptyHint = widget.NewLabel("还没有接入点,点「新增」添加一个")
|
||||
s.emptyHint.Importance = widget.LowImportance
|
||||
s.emptyHint.Hide()
|
||||
|
||||
addBtn := widget.NewButtonWithIcon("新增", theme.ContentAddIcon(), func() { s.askNewFormat() })
|
||||
dupBtn := widget.NewButtonWithIcon("复制", theme.ContentCopyIcon(), func() { s.duplicateProfile() })
|
||||
delBtn := widget.NewButtonWithIcon("删除", theme.DeleteIcon(), func() { s.deleteProfile() })
|
||||
useBtn := widget.NewButton("设为生效", func() { s.activateProfile() })
|
||||
|
||||
top := container.NewVBox(widget.NewLabel("接入点"), widget.NewSeparator())
|
||||
top := container.NewVBox(widget.NewLabel("接入点"), widget.NewSeparator(), s.emptyHint)
|
||||
bottom := container.NewVBox(
|
||||
widget.NewSeparator(),
|
||||
container.NewHBox(addBtn, dupBtn, delBtn),
|
||||
@@ -413,8 +417,8 @@ func (s *settingsWindow) askNewFormat() {
|
||||
// addProfile 按选定格式新增一份接入点。
|
||||
func (s *settingsWindow) addProfile(format string) {
|
||||
p := s.cfg.AddProfile(config.NewProfile("接入点", format))
|
||||
s.profileList.Refresh()
|
||||
s.profileList.Select(s.cfg.ProfileIndex(p.Name))
|
||||
s.selectInList(s.cfg.ProfileIndex(p.Name))
|
||||
s.applyNow("已新增接入点 " + p.Name)
|
||||
}
|
||||
|
||||
// duplicateProfile 复制当前接入点。
|
||||
@@ -427,34 +431,40 @@ func (s *settingsWindow) duplicateProfile() {
|
||||
cp.Name = s.cfg.UniqueName(p.Name + " 副本")
|
||||
cp.ExtraHeaders = copyHeaders(p.ExtraHeaders)
|
||||
s.cfg.Models = append(s.cfg.Models, cp)
|
||||
s.profileList.Refresh()
|
||||
s.profileList.Select(s.cfg.ProfileIndex(cp.Name))
|
||||
s.selectInList(s.cfg.ProfileIndex(cp.Name))
|
||||
s.applyNow("已复制为接入点 " + cp.Name)
|
||||
}
|
||||
|
||||
// selectInList 选中列表里的第 i 项,并把右边的表单切过去。
|
||||
func (s *settingsWindow) selectInList(i int) {
|
||||
if i < 0 || i >= len(s.cfg.Models) {
|
||||
return
|
||||
}
|
||||
s.selIndex = i
|
||||
// 先取消再选,保证 OnSelected 一定回调,表单随之刷新。
|
||||
s.profileList.UnselectAll()
|
||||
s.profileList.Select(i)
|
||||
}
|
||||
|
||||
// deleteProfile 删除当前接入点。
|
||||
// 删除立即生效:直接应用到引擎并落盘,关掉设置窗口也不会"复活"。
|
||||
func (s *settingsWindow) deleteProfile() {
|
||||
p, ok := s.profileAt(s.selIndex)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
dialog.NewConfirm("删除接入点", "确定删除「"+p.Name+"」?", func(confirmed bool) {
|
||||
// 注意用 ShowConfirm:NewConfirm 只创建不显示,点「删除」会毫无反应。
|
||||
dialog.ShowConfirm("删除接入点", "确定删除「"+p.Name+"」?", func(confirmed bool) {
|
||||
if !confirmed {
|
||||
return
|
||||
}
|
||||
s.cfg.RemoveModel(p.Name)
|
||||
s.profileList.Refresh()
|
||||
if !s.cfg.RemoveModel(p.Name) {
|
||||
return
|
||||
}
|
||||
if s.cfg.ProfileIndex(s.cfg.Active) < 0 {
|
||||
s.cfg.Active = ""
|
||||
}
|
||||
if len(s.cfg.Models) == 0 {
|
||||
s.selIndex = -1
|
||||
return
|
||||
}
|
||||
idx := s.selIndex
|
||||
if idx >= len(s.cfg.Models) {
|
||||
idx = len(s.cfg.Models) - 1
|
||||
}
|
||||
s.profileList.Select(idx)
|
||||
s.applyNow("已删除接入点 " + p.Name)
|
||||
}, s.win)
|
||||
}
|
||||
|
||||
@@ -465,7 +475,78 @@ func (s *settingsWindow) activateProfile() {
|
||||
return
|
||||
}
|
||||
s.cfg.Active = p.Name
|
||||
s.applyNow("已切换到接入点 " + p.Name)
|
||||
}
|
||||
|
||||
// applyNow 把当前改动立即应用到引擎并落盘。
|
||||
//
|
||||
// 接入点列表的增删改属于结构变化,必须当场生效:只改设置页里的副本的话,
|
||||
// 关掉窗口就丢了,用户会以为"删不掉"。
|
||||
func (s *settingsWindow) applyNow(notice string) {
|
||||
// 配置里至少要保留一个接入点,删光后 Normalize 会补一个空白项。
|
||||
emptied := len(s.cfg.Models) == 0
|
||||
|
||||
if err := s.app.applyAndSave(s.cfg); err != nil {
|
||||
fyne.Do(func() { s.app.chat.addNotice("操作失败:" + err.Error()) })
|
||||
return
|
||||
}
|
||||
s.syncSelection()
|
||||
s.refreshList()
|
||||
|
||||
if emptied {
|
||||
notice += "(接入点已清空,点「新增」可以再加一个)"
|
||||
}
|
||||
if notice != "" {
|
||||
fyne.Do(func() { s.app.chat.addNotice(notice) })
|
||||
}
|
||||
}
|
||||
|
||||
// syncSelection 让列表选中项、右边表单和配置保持一致。
|
||||
//
|
||||
// 不能只靠 List.Select 的回调:Fyne 在 id 没变时会直接返回,
|
||||
// 不会触发 OnSelected,表单就会继续显示已经删掉的接入点。
|
||||
func (s *settingsWindow) syncSelection() {
|
||||
if len(s.cfg.Models) == 0 {
|
||||
s.selIndex = -1
|
||||
s.profileList.UnselectAll()
|
||||
s.clearProfileForm()
|
||||
return
|
||||
}
|
||||
idx := s.selIndex
|
||||
if idx < 0 || idx >= len(s.cfg.Models) {
|
||||
idx = s.cfg.ProfileIndex(s.cfg.Active)
|
||||
}
|
||||
if idx < 0 || idx >= len(s.cfg.Models) {
|
||||
idx = 0
|
||||
}
|
||||
s.selectInList(idx)
|
||||
}
|
||||
|
||||
// refreshList 刷新列表,并按接入点数量显示/隐藏空列表提示。
|
||||
func (s *settingsWindow) refreshList() {
|
||||
s.profileList.Refresh()
|
||||
if s.emptyHint == nil {
|
||||
return
|
||||
}
|
||||
if len(s.cfg.Models) == 0 {
|
||||
s.emptyHint.Show()
|
||||
} else {
|
||||
s.emptyHint.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
// clearProfileForm 清空右边的表单(一个接入点都没有时用)。
|
||||
func (s *settingsWindow) clearProfileForm() {
|
||||
prev := s.loading
|
||||
s.loading = true
|
||||
s.nameEntry.SetText("")
|
||||
s.baseEntry.SetText("")
|
||||
s.modelEntry.SetText("")
|
||||
s.keyEntry.SetText("")
|
||||
s.maxEntry.SetText("")
|
||||
s.tempEntry.SetText("")
|
||||
s.headerEntry.SetText("")
|
||||
s.loading = prev
|
||||
}
|
||||
|
||||
// testConnection 用当前表单的值测试一次连通性。
|
||||
@@ -523,6 +604,7 @@ func (s *settingsWindow) buildCaptureTab() fyne.CanvasObject {
|
||||
s.maxEdge = widget.NewEntry()
|
||||
s.quality = widget.NewEntry()
|
||||
s.diff = widget.NewEntry()
|
||||
s.sens = widget.NewEntry()
|
||||
s.cooldown = widget.NewEntry()
|
||||
s.stable = widget.NewEntry()
|
||||
s.turns = widget.NewEntry()
|
||||
@@ -532,10 +614,11 @@ func (s *settingsWindow) buildCaptureTab() fyne.CanvasObject {
|
||||
entry("图片长边上限", s.maxEdge),
|
||||
entry("JPEG 质量", s.quality),
|
||||
entry("变化阈值", s.diff),
|
||||
entry("变化灵敏度", s.sens),
|
||||
entry("冷却时间 (ms)", s.cooldown),
|
||||
entry("稳定帧数", s.stable),
|
||||
entry("历史轮数", s.turns),
|
||||
widget.NewLabel("说明:变化阈值越小越灵敏(默认 5);冷却时间决定两次自动提问的最小间隔。"),
|
||||
widget.NewLabel("说明:变化阈值越小越灵敏(默认 5,基于感知哈希);变化灵敏度是细节网格的归一化平均差阈值(0~1,越小越灵敏,默认 0.01),专门用来捕捉「布局没变、只是文字/题目变了」这类更新。两者任一超阈值即触发。"),
|
||||
)
|
||||
|
||||
saveBtn := widget.NewButton("保存并应用", func() {
|
||||
@@ -634,6 +717,7 @@ func (s *settingsWindow) commitCapture() {
|
||||
s.cfg.Capture.MaxEdge = atoiDefault(s.maxEdge.Text, 1280)
|
||||
s.cfg.Capture.JPEGQuality = atoiDefault(s.quality.Text, 75)
|
||||
s.cfg.Capture.DiffThreshold = atoiDefault(s.diff.Text, 5)
|
||||
s.cfg.Capture.DiffSensitivity = atofDefault(s.sens.Text, 0.01)
|
||||
s.cfg.Capture.CooldownMs = atoiDefault(s.cooldown.Text, 3000)
|
||||
s.cfg.Capture.StableFrames = atoiDefault(s.stable.Text, 2)
|
||||
s.cfg.Capture.HistoryTurns = atoiDefault(s.turns.Text, 6)
|
||||
|
||||
Reference in new issue
Block a user