gui/pkg/display/menu.go
Snider 032c426ac3 feat: initial Wails v3 desktop framework
GUI packages, examples, and documentation for building
desktop applications with Go and web technologies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 08:44:23 +00:00

185 lines
4.9 KiB
Go

package display
import (
"fmt"
"runtime"
"strings"
"github.com/wailsapp/wails/v3/pkg/application"
)
// buildMenu creates and sets the main application menu. This function is called
// during the startup of the display service.
func (s *Service) buildMenu() {
appMenu := s.app.Menu().New()
if runtime.GOOS == "darwin" {
appMenu.AddRole(application.AppMenu)
}
appMenu.AddRole(application.FileMenu)
appMenu.AddRole(application.ViewMenu)
appMenu.AddRole(application.EditMenu)
workspace := appMenu.AddSubmenu("Workspace")
workspace.Add("New...").OnClick(func(ctx *application.Context) {
s.handleNewWorkspace()
})
workspace.Add("List").OnClick(func(ctx *application.Context) {
s.handleListWorkspaces()
})
// Developer menu for IDE features
developer := appMenu.AddSubmenu("Developer")
developer.Add("New File").SetAccelerator("CmdOrCtrl+N").OnClick(func(ctx *application.Context) {
s.handleNewFile()
})
developer.Add("Open File...").SetAccelerator("CmdOrCtrl+O").OnClick(func(ctx *application.Context) {
s.handleOpenFile()
})
developer.Add("Save").SetAccelerator("CmdOrCtrl+S").OnClick(func(ctx *application.Context) {
s.handleSaveFile()
})
developer.AddSeparator()
developer.Add("Editor").OnClick(func(ctx *application.Context) {
s.handleOpenEditor()
})
developer.Add("Terminal").OnClick(func(ctx *application.Context) {
s.handleOpenTerminal()
})
developer.AddSeparator()
developer.Add("Run").SetAccelerator("CmdOrCtrl+R").OnClick(func(ctx *application.Context) {
s.handleRun()
})
developer.Add("Build").SetAccelerator("CmdOrCtrl+B").OnClick(func(ctx *application.Context) {
s.handleBuild()
})
appMenu.AddRole(application.WindowMenu)
appMenu.AddRole(application.HelpMenu)
s.app.Menu().Set(appMenu)
}
// handleNewWorkspace opens a window for creating a new workspace.
func (s *Service) handleNewWorkspace() {
// Open a dedicated window for workspace creation
// The frontend at /workspace/new handles the form
opts := application.WebviewWindowOptions{
Name: "workspace-new",
Title: "New Workspace",
Width: 500,
Height: 400,
URL: "/workspace/new",
}
s.app.Window().NewWithOptions(opts)
}
// handleListWorkspaces shows a dialog with available workspaces.
func (s *Service) handleListWorkspaces() {
// Get workspace service from core
ws := s.Core().Service("workspace")
if ws == nil {
dialog := s.app.Dialog().Warning()
dialog.SetTitle("Workspace")
dialog.SetMessage("Workspace service not available")
dialog.Show()
return
}
// Type assert to access ListWorkspaces method
lister, ok := ws.(interface{ ListWorkspaces() []string })
if !ok {
dialog := s.app.Dialog().Warning()
dialog.SetTitle("Workspace")
dialog.SetMessage("Unable to list workspaces")
dialog.Show()
return
}
workspaces := lister.ListWorkspaces()
var message string
if len(workspaces) == 0 {
message = "No workspaces found.\n\nUse Workspace → New to create one."
} else {
message = fmt.Sprintf("Available Workspaces (%d):\n\n%s",
len(workspaces),
strings.Join(workspaces, "\n"))
}
dialog := s.app.Dialog().Info()
dialog.SetTitle("Workspaces")
dialog.SetMessage(message)
dialog.Show()
}
// handleNewFile opens the editor with a new untitled file.
func (s *Service) handleNewFile() {
opts := application.WebviewWindowOptions{
Name: "editor",
Title: "New File - Editor",
Width: 1200,
Height: 800,
URL: "/#/developer/editor?new=true",
}
s.app.Window().NewWithOptions(opts)
}
// handleOpenFile opens a file dialog to select a file, then opens it in the editor.
func (s *Service) handleOpenFile() {
dialog := s.app.Dialog().OpenFile()
dialog.SetTitle("Open File")
dialog.CanChooseFiles(true)
dialog.CanChooseDirectories(false)
result, err := dialog.PromptForSingleSelection()
if err != nil || result == "" {
return
}
opts := application.WebviewWindowOptions{
Name: "editor",
Title: result + " - Editor",
Width: 1200,
Height: 800,
URL: "/#/developer/editor?file=" + result,
}
s.app.Window().NewWithOptions(opts)
}
// handleSaveFile emits a save event to the focused editor window.
func (s *Service) handleSaveFile() {
s.app.Event().Emit("ide:save")
}
// handleOpenEditor opens a standalone editor window.
func (s *Service) handleOpenEditor() {
opts := application.WebviewWindowOptions{
Name: "editor",
Title: "Editor",
Width: 1200,
Height: 800,
URL: "/#/developer/editor",
}
s.app.Window().NewWithOptions(opts)
}
// handleOpenTerminal opens a terminal window.
func (s *Service) handleOpenTerminal() {
opts := application.WebviewWindowOptions{
Name: "terminal",
Title: "Terminal",
Width: 800,
Height: 500,
URL: "/#/developer/terminal",
}
s.app.Window().NewWithOptions(opts)
}
// handleRun emits a run event that the IDE service can handle.
func (s *Service) handleRun() {
s.app.Event().Emit("ide:run")
}
// handleBuild emits a build event that the IDE service can handle.
func (s *Service) handleBuild() {
s.app.Event().Emit("ide:build")
}