waveterm/pkg/wcore/window.go
Evan Simkowitz ec4f6c01de
Create workspace will always create a saved workspace (#1509)
Also moves icon and color definitions to the backend and makes it so the
new workspaces get created with a different icon color for each index.

If an ephemeral window has more than one tab, always create a new window
when switching workspaces

Also fixes workspace accelerators on macOS
2024-12-12 17:21:09 -08:00

206 lines
6.2 KiB
Go

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package wcore
import (
"context"
"fmt"
"log"
"github.com/google/uuid"
"github.com/wavetermdev/waveterm/pkg/eventbus"
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
"github.com/wavetermdev/waveterm/pkg/wshutil"
"github.com/wavetermdev/waveterm/pkg/wstore"
)
func SwitchWorkspace(ctx context.Context, windowId string, workspaceId string) (*waveobj.Workspace, error) {
log.Printf("SwitchWorkspace %s %s\n", windowId, workspaceId)
ws, err := GetWorkspace(ctx, workspaceId)
if err != nil {
return nil, fmt.Errorf("error getting new workspace: %w", err)
}
window, err := GetWindow(ctx, windowId)
if err != nil {
return nil, fmt.Errorf("error getting window: %w", err)
}
if window.WorkspaceId == workspaceId {
return nil, nil
}
allWindows, err := wstore.DBGetAllObjsByType[*waveobj.Window](ctx, waveobj.OType_Window)
if err != nil {
return nil, fmt.Errorf("error getting all windows: %w", err)
}
for _, w := range allWindows {
if w.WorkspaceId == workspaceId {
log.Printf("workspace %s already has a window %s, focusing that window\n", workspaceId, w.OID)
client := wshclient.GetBareRpcClient()
err = wshclient.FocusWindowCommand(client, w.OID, &wshrpc.RpcOpts{Route: wshutil.ElectronRoute})
return nil, err
}
}
curWs, err := GetWorkspace(ctx, window.WorkspaceId)
if err != nil {
return nil, fmt.Errorf("error getting current workspace: %w", err)
}
deleted, err := DeleteWorkspace(ctx, curWs.OID, false)
if err != nil {
return nil, fmt.Errorf("error deleting current workspace: %w", err)
}
if !deleted {
log.Printf("current workspace %s was not deleted\n", curWs.OID)
} else {
log.Printf("deleted current workspace %s\n", curWs.OID)
}
window.WorkspaceId = workspaceId
log.Printf("switching window %s to workspace %s\n", windowId, workspaceId)
return ws, wstore.DBUpdate(ctx, window)
}
func GetWindow(ctx context.Context, windowId string) (*waveobj.Window, error) {
window, err := wstore.DBMustGet[*waveobj.Window](ctx, windowId)
if err != nil {
log.Printf("error getting window %q: %v\n", windowId, err)
return nil, err
}
return window, nil
}
func CreateWindow(ctx context.Context, winSize *waveobj.WinSize, workspaceId string) (*waveobj.Window, error) {
log.Printf("CreateWindow %v %v\n", winSize, workspaceId)
var ws *waveobj.Workspace
if workspaceId == "" {
ws1, err := CreateWorkspace(ctx, "", "", "", false, false)
if err != nil {
return nil, fmt.Errorf("error creating workspace: %w", err)
}
ws = ws1
} else {
ws1, err := GetWorkspace(ctx, workspaceId)
if err != nil {
return nil, fmt.Errorf("error getting workspace: %w", err)
}
ws = ws1
}
windowId := uuid.NewString()
if winSize == nil {
winSize = &waveobj.WinSize{
Width: 0,
Height: 0,
}
}
window := &waveobj.Window{
OID: windowId,
WorkspaceId: ws.OID,
IsNew: true,
Pos: waveobj.Point{
X: 0,
Y: 0,
},
WinSize: *winSize,
}
err := wstore.DBInsert(ctx, window)
if err != nil {
return nil, fmt.Errorf("error inserting window: %w", err)
}
client, err := GetClientData(ctx)
if err != nil {
return nil, fmt.Errorf("error getting client: %w", err)
}
client.WindowIds = append(client.WindowIds, windowId)
err = wstore.DBUpdate(ctx, client)
if err != nil {
return nil, fmt.Errorf("error updating client: %w", err)
}
return GetWindow(ctx, windowId)
}
// CloseWindow closes a window and deletes its workspace if it is empty and not named.
// If fromElectron is true, it does not send an event to Electron.
func CloseWindow(ctx context.Context, windowId string, fromElectron bool) error {
log.Printf("CloseWindow %s\n", windowId)
window, err := GetWindow(ctx, windowId)
if err == nil {
log.Printf("got window %s\n", windowId)
deleted, err := DeleteWorkspace(ctx, window.WorkspaceId, false)
if err != nil {
log.Printf("error deleting workspace: %v\n", err)
}
if deleted {
log.Printf("deleted workspace %s\n", window.WorkspaceId)
}
err = wstore.DBDelete(ctx, waveobj.OType_Window, windowId)
if err != nil {
return fmt.Errorf("error deleting window: %w", err)
}
log.Printf("deleted window %s\n", windowId)
} else {
log.Printf("error getting window %s: %v\n", windowId, err)
}
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
if err != nil {
return fmt.Errorf("error getting client: %w", err)
}
client.WindowIds = utilfn.RemoveElemFromSlice(client.WindowIds, windowId)
err = wstore.DBUpdate(ctx, client)
if err != nil {
return fmt.Errorf("error updating client: %w", err)
}
log.Printf("updated client\n")
if !fromElectron {
eventbus.SendEventToElectron(eventbus.WSEventType{
EventType: eventbus.WSEvent_ElectronCloseWindow,
Data: windowId,
})
}
return nil
}
func CheckAndFixWindow(ctx context.Context, windowId string) *waveobj.Window {
log.Printf("CheckAndFixWindow %s\n", windowId)
window, err := GetWindow(ctx, windowId)
if err != nil {
log.Printf("error getting window %q (in checkAndFixWindow): %v\n", windowId, err)
return nil
}
ws, err := GetWorkspace(ctx, window.WorkspaceId)
if err != nil {
log.Printf("error getting workspace %q (in checkAndFixWindow): %v\n", window.WorkspaceId, err)
CloseWindow(ctx, windowId, false)
return nil
}
if len(ws.TabIds) == 0 && len(ws.PinnedTabIds) == 0 {
log.Printf("fixing workspace with no tabs %q (in checkAndFixWindow)\n", ws.OID)
_, err = CreateTab(ctx, ws.OID, "", true, false, false)
if err != nil {
log.Printf("error creating tab (in checkAndFixWindow): %v\n", err)
}
}
return window
}
func FocusWindow(ctx context.Context, windowId string) error {
log.Printf("FocusWindow %s\n", windowId)
client, err := GetClientData(ctx)
if err != nil {
log.Printf("error getting client data: %v\n", err)
return err
}
winIdx := utilfn.SliceIdx(client.WindowIds, windowId)
if winIdx == -1 {
log.Printf("window %s not found in client data\n", windowId)
return nil
}
client.WindowIds = utilfn.MoveSliceIdxToFront(client.WindowIds, winIdx)
log.Printf("client.WindowIds: %v\n", client.WindowIds)
return wstore.DBUpdate(ctx, client)
}