desktop.go
1 package desktop 2 3 import ( 4 "fmt" 5 "image/color" 6 "krillin-ai/config" 7 "krillin-ai/log" 8 9 "fyne.io/fyne/v2" 10 "fyne.io/fyne/v2/app" 11 "fyne.io/fyne/v2/canvas" 12 "fyne.io/fyne/v2/container" 13 "fyne.io/fyne/v2/dialog" 14 "fyne.io/fyne/v2/layout" 15 "fyne.io/fyne/v2/theme" 16 "fyne.io/fyne/v2/widget" 17 "go.uber.org/zap" 18 ) 19 20 func createNavButton(text string, icon fyne.Resource, isSelected bool, onTap func()) *widget.Button { 21 btn := widget.NewButtonWithIcon(text, icon, onTap) 22 23 // 根据选中状态设置颜色 24 if isSelected { 25 btn.Importance = widget.HighImportance 26 } else { 27 btn.Importance = widget.LowImportance 28 } 29 30 return btn 31 } 32 33 // Show 展示桌面 34 func Show() { 35 myApp := app.New() 36 myWindow := myApp.NewWindow("KrillinAI") 37 38 // 创建主题管理器 39 themeManager := NewThemeManager(myApp, myWindow) 40 41 // 设置全局主题管理器 42 SetGlobalThemeManager(themeManager) 43 44 logoContainer := container.NewVBox() 45 46 logo := canvas.NewText("KrillinAI", color.NRGBA{R: 59, G: 130, B: 246, A: 255}) 47 logo.TextSize = 28 48 logo.TextStyle = fyne.TextStyle{Bold: true} 49 logo.Alignment = fyne.TextAlignCenter 50 51 separator := canvas.NewRectangle(color.NRGBA{R: 209, G: 213, B: 219, A: 255}) 52 separator.SetMinSize(fyne.NewSize(0, 2)) 53 54 slogan := canvas.NewText("AI Video Translation & Dubbing by Krillin AI", color.NRGBA{R: 107, G: 114, B: 128, A: 255}) 55 slogan.TextSize = 12 56 slogan.Alignment = fyne.TextAlignCenter 57 58 logoContainer.Add(logo) 59 logoContainer.Add(separator) 60 logoContainer.Add(slogan) 61 62 navItems := []string{"工作台 Dashboard", "LLM 配置", "配置 Settings"} 63 navIcons := []fyne.Resource{theme.DocumentIcon(), theme.ComputerIcon(), theme.SettingsIcon()} 64 65 var navButtons []*widget.Button 66 navContainer := container.NewVBox() 67 68 contentStack := container.NewStack() 69 currentSelectedIndex := 0 70 71 var workbenchContent, llmContent, configContent fyne.CanvasObject 72 73 var refreshContent = func() { 74 contentStack.Objects = []fyne.CanvasObject{} 75 76 workbenchContent = CreateSubtitleTab(myWindow) 77 llmContent = CreateLlmTab() 78 configContent = CreateConfigTab(myWindow) 79 80 switch currentSelectedIndex { 81 case 0: 82 contentStack.Add(workbenchContent) 83 contentStack.Add(llmContent) 84 contentStack.Add(configContent) 85 llmContent.Hide() 86 configContent.Hide() 87 case 1: 88 contentStack.Add(workbenchContent) 89 contentStack.Add(llmContent) 90 contentStack.Add(configContent) 91 workbenchContent.Hide() 92 configContent.Hide() 93 case 2: 94 contentStack.Add(workbenchContent) 95 contentStack.Add(llmContent) 96 contentStack.Add(configContent) 97 workbenchContent.Hide() 98 llmContent.Hide() 99 } 100 101 contentStack.Refresh() 102 } 103 104 refreshContent() 105 106 // 添加主题切换回调来刷新内容 107 themeManager.AddThemeChangeCallback(func(isDark bool) { 108 refreshContent() 109 }) 110 111 for i, item := range navItems { 112 index := i // 捕获变量 113 isSelected := i == currentSelectedIndex 114 115 navBtn := createNavButton(item, navIcons[i], isSelected, func() { 116 if currentSelectedIndex == index { 117 return 118 } 119 120 for j, btn := range navButtons { 121 if j == index { 122 btn.Importance = widget.HighImportance 123 } else { 124 btn.Importance = widget.LowImportance 125 } 126 } 127 128 // 保存配置并切换界面 129 err := config.SaveConfig() 130 if err != nil { 131 dialog.ShowError(fmt.Errorf("保存配置失败: %v", err), myWindow) 132 } 133 134 switch index { 135 case 0: 136 workbenchContent.Show() 137 llmContent.Hide() 138 configContent.Hide() 139 case 1: 140 workbenchContent.Hide() 141 llmContent.Show() 142 configContent.Hide() 143 case 2: 144 workbenchContent.Hide() 145 llmContent.Hide() 146 configContent.Show() 147 } 148 149 currentSelectedIndex = index 150 }) 151 152 navButtons = append(navButtons, navBtn) 153 navContainer.Add(navBtn) 154 } 155 156 themeToggleContainer := themeManager.CreateThemeToggleButton() 157 158 navBottomContainer := container.NewVBox( 159 layout.NewSpacer(), 160 themeToggleContainer, 161 ) 162 163 navBackground := themeManager.CreateGlassmorphismBackground() 164 165 navWithBackground := container.NewStack( 166 navBackground, 167 container.NewBorder( 168 container.NewPadded(logoContainer), 169 container.NewPadded(navBottomContainer), 170 nil, nil, 171 container.NewPadded(navContainer), 172 ), 173 ) 174 175 split := container.NewHSplit(navWithBackground, container.NewPadded(contentStack)) 176 split.SetOffset(0.25) 177 178 mainContainer := container.NewPadded(split) 179 180 var statusTextColor, statusBgColor color.Color 181 if themeManager.IsDarkMode() { 182 statusTextColor = color.NRGBA{R: 148, G: 163, B: 184, A: 180} 183 statusBgColor = color.NRGBA{R: 30, G: 41, B: 59, A: 150} 184 } else { 185 statusTextColor = color.NRGBA{R: 107, G: 114, B: 128, A: 180} 186 statusBgColor = color.NRGBA{R: 255, G: 255, B: 255, A: 150} 187 } 188 189 statusText := canvas.NewText("就绪", statusTextColor) 190 statusText.TextSize = 12 191 192 statusBarBackground := canvas.NewRectangle(statusBgColor) 193 statusBarBackground.CornerRadius = 8 194 195 statusBar := container.NewStack( 196 statusBarBackground, 197 container.NewHBox( 198 layout.NewSpacer(), 199 statusText, 200 ), 201 ) 202 203 finalContainer := container.NewBorder(nil, container.NewPadded(statusBar), nil, nil, mainContainer) 204 205 myWindow.SetContent(finalContainer) 206 myWindow.Resize(fyne.NewSize(1200, 800)) 207 myWindow.CenterOnScreen() 208 myWindow.ShowAndRun() 209 210 // 关闭窗口时保存配置 211 err := config.SaveConfig() 212 if err != nil { 213 log.GetLogger().Error("保存配置失败 Failed to save config", zap.Error(err)) 214 return 215 } 216 log.GetLogger().Info("配置已保存 Config saved successfully") 217 }