/ internal / desktop / desktop.go
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  }