mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-07 19:28:44 +01:00
aa77b2c259
![image](https://github.com/user-attachments/assets/a4072368-b204-4eed-bb65-8e3884687f9a) This functions very similarly to VSCode's pinned tab feature. To pin a tab, you can right-click on it and select "Pin tab" from the context menu. Once pinned, a tab will be fixed to the left-most edge of the tab bar, in order of pinning. Pinned tabs can be dragged around like any others. If you drag an unpinned tab into the pinned tabs section (any index less than the highest-index pinned tab), it will be pinned. If you drag a pinned tab out of the pinned tab section, it will be unpinned. Pinned tabs' close button is replaced with a persistent pin button, which can be clicked to unpin them. This adds an extra barrier to accidentally closing a pinned tab. They can still be closed from the context menu.
189 lines
5.7 KiB
Go
189 lines
5.7 KiB
Go
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package windowservice
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/eventbus"
|
|
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
|
"github.com/wavetermdev/waveterm/pkg/tsgen/tsgenmeta"
|
|
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
|
"github.com/wavetermdev/waveterm/pkg/wcore"
|
|
"github.com/wavetermdev/waveterm/pkg/wlayout"
|
|
"github.com/wavetermdev/waveterm/pkg/wps"
|
|
"github.com/wavetermdev/waveterm/pkg/wstore"
|
|
)
|
|
|
|
const DefaultTimeout = 2 * time.Second
|
|
|
|
type WindowService struct{}
|
|
|
|
func (svc *WindowService) GetWindow_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
ArgNames: []string{"windowId"},
|
|
}
|
|
}
|
|
|
|
func (svc *WindowService) GetWindow(windowId string) (*waveobj.Window, error) {
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
|
defer cancelFn()
|
|
window, err := wstore.DBGet[*waveobj.Window](ctx, windowId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting window: %w", err)
|
|
}
|
|
return window, nil
|
|
}
|
|
|
|
func (svc *WindowService) CreateWindow_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
ArgNames: []string{"ctx", "winSize", "workspaceId"},
|
|
}
|
|
}
|
|
|
|
func (svc *WindowService) CreateWindow(ctx context.Context, winSize *waveobj.WinSize, workspaceId string) (*waveobj.Window, error) {
|
|
window, err := wcore.CreateWindow(ctx, winSize, workspaceId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating window: %w", err)
|
|
}
|
|
ws, err := wcore.GetWorkspace(ctx, window.WorkspaceId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting workspace: %w", err)
|
|
}
|
|
if len(ws.TabIds) == 0 {
|
|
_, err = wcore.CreateTab(ctx, ws.OID, "", true, false)
|
|
if err != nil {
|
|
return window, fmt.Errorf("error creating tab: %w", err)
|
|
}
|
|
ws, err = wcore.GetWorkspace(ctx, window.WorkspaceId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting updated workspace: %w", err)
|
|
}
|
|
err = wlayout.BootstrapNewWorkspaceLayout(ctx, ws)
|
|
if err != nil {
|
|
return window, fmt.Errorf("error bootstrapping new workspace layout: %w", err)
|
|
}
|
|
}
|
|
return window, nil
|
|
}
|
|
|
|
func (svc *WindowService) SetWindowPosAndSize_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
Desc: "set window position and size",
|
|
ArgNames: []string{"ctx", "windowId", "pos", "size"},
|
|
}
|
|
}
|
|
|
|
func (ws *WindowService) SetWindowPosAndSize(ctx context.Context, windowId string, pos *waveobj.Point, size *waveobj.WinSize) (waveobj.UpdatesRtnType, error) {
|
|
if pos == nil && size == nil {
|
|
return nil, nil
|
|
}
|
|
ctx = waveobj.ContextWithUpdates(ctx)
|
|
win, err := wstore.DBMustGet[*waveobj.Window](ctx, windowId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if pos != nil {
|
|
win.Pos = *pos
|
|
}
|
|
if size != nil {
|
|
win.WinSize = *size
|
|
}
|
|
win.IsNew = false
|
|
err = wstore.DBUpdate(ctx, win)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return waveobj.ContextGetUpdatesRtn(ctx), nil
|
|
}
|
|
|
|
func (svc *WindowService) MoveBlockToNewWindow_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
Desc: "move block to new window",
|
|
ArgNames: []string{"ctx", "currentTabId", "blockId"},
|
|
}
|
|
}
|
|
|
|
func (svc *WindowService) MoveBlockToNewWindow(ctx context.Context, currentTabId string, blockId string) (waveobj.UpdatesRtnType, error) {
|
|
log.Printf("MoveBlockToNewWindow(%s, %s)", currentTabId, blockId)
|
|
ctx = waveobj.ContextWithUpdates(ctx)
|
|
tab, err := wstore.DBMustGet[*waveobj.Tab](ctx, currentTabId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting tab: %w", err)
|
|
}
|
|
log.Printf("tab.BlockIds[%s]: %v", tab.OID, tab.BlockIds)
|
|
var foundBlock bool
|
|
for _, tabBlockId := range tab.BlockIds {
|
|
if tabBlockId == blockId {
|
|
foundBlock = true
|
|
break
|
|
}
|
|
}
|
|
if !foundBlock {
|
|
return nil, fmt.Errorf("block not found in current tab")
|
|
}
|
|
newWindow, err := wcore.CreateWindow(ctx, nil, "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating window: %w", err)
|
|
}
|
|
ws, err := wcore.GetWorkspace(ctx, newWindow.WorkspaceId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting workspace: %w", err)
|
|
}
|
|
err = wstore.MoveBlockToTab(ctx, currentTabId, ws.ActiveTabId, blockId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error moving block to tab: %w", err)
|
|
}
|
|
eventbus.SendEventToElectron(eventbus.WSEventType{
|
|
EventType: eventbus.WSEvent_ElectronNewWindow,
|
|
Data: newWindow.OID,
|
|
})
|
|
windowCreated := eventbus.BusyWaitForWindowId(newWindow.OID, 2*time.Second)
|
|
if !windowCreated {
|
|
return nil, fmt.Errorf("new window not created")
|
|
}
|
|
wlayout.QueueLayoutActionForTab(ctx, currentTabId, waveobj.LayoutActionData{
|
|
ActionType: wlayout.LayoutActionDataType_Remove,
|
|
BlockId: blockId,
|
|
})
|
|
wlayout.QueueLayoutActionForTab(ctx, ws.ActiveTabId, waveobj.LayoutActionData{
|
|
ActionType: wlayout.LayoutActionDataType_Insert,
|
|
BlockId: blockId,
|
|
Focused: true,
|
|
})
|
|
return waveobj.ContextGetUpdatesRtn(ctx), nil
|
|
}
|
|
|
|
func (svc *WindowService) SwitchWorkspace_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
ArgNames: []string{"ctx", "windowId", "workspaceId"},
|
|
}
|
|
}
|
|
|
|
func (svc *WindowService) SwitchWorkspace(ctx context.Context, windowId string, workspaceId string) (*waveobj.Workspace, error) {
|
|
ctx = waveobj.ContextWithUpdates(ctx)
|
|
ws, err := wcore.SwitchWorkspace(ctx, windowId, workspaceId)
|
|
|
|
updates := waveobj.ContextGetUpdatesRtn(ctx)
|
|
go func() {
|
|
defer panichandler.PanicHandler("WindowService:SwitchWorkspace:SendUpdateEvents")
|
|
wps.Broker.SendUpdateEvents(updates)
|
|
}()
|
|
return ws, err
|
|
}
|
|
|
|
func (svc *WindowService) CloseWindow_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
ArgNames: []string{"ctx", "windowId", "fromElectron"},
|
|
}
|
|
}
|
|
|
|
func (svc *WindowService) CloseWindow(ctx context.Context, windowId string, fromElectron bool) error {
|
|
ctx = waveobj.ContextWithUpdates(ctx)
|
|
return wcore.CloseWindow(ctx, windowId, fromElectron)
|
|
}
|