akiyosi.goneovim/editor/editor.go

1357 lines
36 KiB
Go
Raw Permalink Normal View History

2017-11-09 02:11:05 +00:00
package editor
2017-03-14 01:52:44 +00:00
import (
2022-06-13 21:08:53 +09:00
"context"
2017-03-14 01:52:44 +00:00
"fmt"
"io"
"log"
2018-01-08 10:23:16 +00:00
"os"
"os/exec"
2018-01-08 10:23:16 +00:00
"path/filepath"
"regexp"
2017-05-12 04:07:54 +01:00
"runtime"
2018-01-08 10:23:16 +00:00
"strconv"
2017-03-14 07:20:31 +00:00
"strings"
2017-06-02 10:41:28 +01:00
"sync"
"time"
2017-03-14 01:52:44 +00:00
"github.com/akiyosi/goneovim/util"
2019-03-13 11:50:52 +09:00
frameless "github.com/akiyosi/goqtframelesswindow"
2023-07-16 00:53:36 +09:00
"github.com/akiyosi/qt/core"
"github.com/akiyosi/qt/gui"
"github.com/akiyosi/qt/widgets"
2025-06-10 20:39:23 +09:00
// "github.com/felixge/fgprof"
2019-11-19 00:32:34 +09:00
homedir "github.com/mitchellh/go-homedir"
2023-01-26 01:33:47 +09:00
"github.com/neovim/go-client/nvim"
2017-03-14 01:52:44 +00:00
)
var editor *Editor
2019-10-22 22:50:56 +09:00
const (
2023-11-16 22:43:10 +09:00
WORKSPACELEN = 10
NVIMCALLTIMEOUT = 320
2023-12-13 22:26:33 +09:00
NVIMCALLTIMEOUT2 = 45
2019-10-22 22:50:56 +09:00
)
2020-12-12 01:39:52 +09:00
type editorSignal struct {
core.QObject
_ func() `signal:"notifySignal"`
_ func() `signal:"sidebarSignal"`
_ func() `signal:"geometrySignal"`
2020-12-12 01:39:52 +09:00
}
2018-12-27 21:03:21 +09:00
// ColorPalette is
2018-12-27 12:38:42 +09:00
type ColorPalette struct {
2019-06-12 22:14:48 +09:00
e *Editor
2019-06-12 21:58:35 +09:00
2018-12-31 16:43:04 +09:00
fg *RGBA
bg *RGBA
inactiveFg *RGBA
comment *RGBA
abyss *RGBA
matchFg *RGBA
selectedBg *RGBA
sideBarFg *RGBA
sideBarBg *RGBA
sideBarSelectedItemBg *RGBA
scrollBarFg *RGBA
scrollBarBg *RGBA
widgetFg *RGBA
widgetBg *RGBA
widgetInputArea *RGBA
minimapCurrentRegion *RGBA
2019-05-31 16:41:37 +09:00
windowSeparator *RGBA
2019-06-07 02:00:14 +09:00
indentGuide *RGBA
2018-12-27 12:38:42 +09:00
}
2019-06-30 19:35:25 +09:00
// NotifyButton is
type NotifyButton struct {
action func()
text string
}
2018-12-21 12:18:41 +09:00
// Notify is
2018-08-16 19:31:36 +09:00
type Notify struct {
message string
buttons []*NotifyButton
2021-12-10 21:24:04 +09:00
level NotifyLevel
period int
2018-08-16 19:31:36 +09:00
}
type Options struct {
Geometry string `long:"geometry" description:"Initial window geometry [e.g. --geometry=800x600]"`
2022-04-19 11:49:35 +09:00
Server string `long:"server" description:"Remote session address [e.g. --server=host:3456]"`
Ssh string `long:"ssh" description:"Attaching to a remote nvim via ssh. Default port is 22. [e.g. --ssh=user@host:port]"`
Nvim string `long:"nvim" description:"Executable nvim path to attach [e.g. --nvim=/path/to/nvim]"`
2022-04-19 11:49:35 +09:00
Debug string `long:"debug" description:"Run debug mode with debug.log(default) file [e.g. --debug=/path/to/my-debug.log]" optional:"yes" optional-value:"debug.log"`
Fullscreen bool `long:"fullscreen" description:"Open the window in fullscreen on startup"`
Maximized bool `long:"maximized" description:"Maximize the window on startup"`
Exttabline bool `long:"exttabline" description:"Externalize the tabline"`
Extcmdline bool `long:"extcmdline" description:"Externalize the cmdline"`
Extmessages bool `long:"extmessages" description:"Externalize the messages. Sets --extcmdline implicitly"`
Extpopupmenu bool `long:"extpopupmenu" description:"Externalize the popupmenu"`
Version bool `long:"version" description:"Print Goneovim version"`
Wsl *string `long:"wsl" description:"Attach to nvim process in wsl environment with distribution(default) [e.g. --wsl=Ubuntu]" optional:"yes" optional-value:""`
Nofork bool `long:"nofork" description:"Run in foreground"`
NoConfig bool `long:"noconfig" description:"Run Goneovim with no config. (Equivalent to loading an empty settings.toml)"`
2019-11-13 22:49:25 +09:00
}
2017-03-14 01:52:44 +00:00
// Editor is the editor
type Editor struct {
stop chan int
signal *editorSignal
ctx context.Context
app *widgets.QApplication
widget *widgets.QWidget
splitter *widgets.QSplitter
window *frameless.QFramelessWindow
specialKeys map[core.Qt__Key]string
svgs map[string]*SvgXML
notifyStartPos *core.QPoint
colors *ColorPalette
notify chan *Notify
cbChan chan *string
chUiPrepared chan bool
openingFileCh chan string
geometryUpdateTimer *time.Timer
sysTray *widgets.QSystemTrayIcon
side *WorkspaceSide
savedGeometry *core.QByteArray
prefixToMapMetaKey string
configDir string
homeDir string
version string
config gonvimConfig
opts Options
font *Font
2023-11-02 22:41:48 +09:00
fallbackfonts []*Font
2025-03-07 16:59:28 +09:00
fontCh chan []*Font
fontErrors []string
notifications []*Notification
workspaces []*Workspace
args []string
keyControl core.Qt__Key
keyCmd core.Qt__Key
windowSize [2]int
width int
active int
startuptime int64
iconSize int
height int
notificationWidth int
stopOnce sync.Once
muMetaKey sync.Mutex
geometryUpdateMutex sync.RWMutex
doRestoreSessions bool
initialColumns int
initialLines int
isSetColumns bool
isSetLines bool
isSetGuiColor bool
isDisplayNotifications bool
isKeyAutoRepeating bool
isWindowResized bool
isWindowNowActivated bool
isWindowNowInactivated bool
isExtWinNowActivated bool
isExtWinNowInactivated bool
isHideMouse bool
isBindNvimSizeToAppwin bool
isUiPrepared bool
isWindowMaximizing bool
2017-06-16 08:43:05 +01:00
}
2017-03-14 01:52:44 +00:00
func (hl *Highlight) copy() Highlight {
highlight := Highlight{}
if hl.foreground != nil {
highlight.foreground = hl.foreground.copy()
}
if hl.background != nil {
highlight.background = hl.background.copy()
}
2018-05-12 22:48:15 +09:00
highlight.bold = hl.bold
highlight.italic = hl.italic
2017-03-14 01:52:44 +00:00
return highlight
}
2017-06-06 02:42:11 +01:00
2017-11-09 02:11:05 +00:00
// InitEditor is
2021-08-06 23:13:30 +09:00
func InitEditor(options Options, args []string) {
2022-12-28 23:22:33 +09:00
// --------------------
2023-01-26 01:33:47 +09:00
// For profiling goneovim
2022-12-28 23:22:33 +09:00
// --------------------
//
// https://blog.golang.org/pprof
// After running the app, exec the following:
// $ go tool pprof -http=localhost:9090 cpuprofile
//
// Comment out the following::
2023-08-16 23:54:23 +09:00
// // * built-in net/http/pprof
2022-12-28 23:22:33 +09:00
// f, ferr := os.Create("cpuprofile")
// if ferr != nil {
// os.Exit(1)
// }
// pprof.StartCPUProfile(f)
2023-08-16 23:54:23 +09:00
//
// g, ferr := os.Create("memprofile")
// if ferr != nil {
// os.Exit(1)
// }
2022-12-28 23:22:33 +09:00
// // * https://github.com/felixge/fgprof
// f, ferr := os.Create("cpuprofile")
// if ferr != nil {
// os.Exit(1)
// }
// fgprofStop := fgprof.Start(f, fgprof.FormatPprof)
editor = &Editor{
2023-01-26 01:33:47 +09:00
version: Version,
args: args,
opts: options,
startuptime: time.Now().UnixNano() / 1000,
signal: NewEditorSignal(nil),
stop: make(chan int),
notify: make(chan *Notify, 10),
cbChan: make(chan *string, 240),
chUiPrepared: make(chan bool, 1),
}
e := editor
2023-01-26 01:33:47 +09:00
// Prepare debug log
e.setDebuglog()
e.putLog("--- GONEOVIM STARTING ---")
2020-12-04 23:14:06 +09:00
2022-06-13 21:08:53 +09:00
ctx, cancel := context.WithCancel(context.Background())
2023-01-26 01:33:47 +09:00
e.ctx = ctx
2022-06-13 21:08:53 +09:00
2023-01-26 01:33:47 +09:00
var err error
2020-08-01 14:22:34 +09:00
// detect home dir
2023-01-26 01:33:47 +09:00
e.homeDir, err = homedir.Dir()
if err != nil {
2023-01-26 01:33:47 +09:00
e.homeDir = "~"
}
2023-03-26 00:21:01 +09:00
e.putLog("detecting home directory path:", e.homeDir)
2023-01-26 01:33:47 +09:00
// load config
e.configDir, e.config = newConfig(e.homeDir, e.opts.NoConfig)
2023-03-26 00:21:01 +09:00
e.putLog("Detecting the goneovim configuration directory:", e.configDir)
2023-01-26 01:33:47 +09:00
e.overwriteConfigByCLIOption()
2020-12-04 23:14:06 +09:00
2023-01-26 01:33:47 +09:00
// put shell environment
e.setEnvironmentVariables()
// set application working directory path
e.setAppDirPath(e.homeDir)
2023-01-26 01:33:47 +09:00
// create qapplication
e.putLog("start generating the application")
core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
2019-11-24 12:52:15 +09:00
e.app = widgets.NewQApplication(len(os.Args), os.Args)
setMyApplicationDelegate()
e.app.SetDoubleClickInterval(0)
e.putLog("finished generating the application")
2025-03-07 16:59:28 +09:00
e.initNotifications()
var cerr, lerr error
e.initialColumns, cerr, e.initialLines, lerr = parseLinesAndColumns(args)
if cerr == nil {
editor.isSetColumns = true
}
if lerr == nil {
editor.isSetLines = true
}
2023-01-26 01:33:47 +09:00
// new nvim instance
signal, redrawUpdates, guiUpdates, nvimCh, uiRCh, errCh := newNvim(
e.initialColumns,
e.initialLines,
e.ctx,
)
2020-12-27 21:32:52 +09:00
2022-04-23 01:31:11 +09:00
// e.setAppDirPath(home)
2023-11-02 22:41:48 +09:00
e.fontCh = make(chan []*Font, 100)
2020-12-05 14:36:12 +09:00
go func() {
2023-11-02 22:41:48 +09:00
e.fontCh <- parseFont(
e.config.Editor.FontFamily,
e.config.Editor.FontSize,
2023-11-02 22:41:48 +09:00
e.config.Editor.FontWeight,
e.config.Editor.FontStretch,
e.config.Editor.Linespace,
2021-11-30 23:15:52 +09:00
e.config.Editor.Letterspace,
2020-12-05 14:36:12 +09:00
)
}()
2020-12-11 21:59:47 +09:00
e.initSVGS()
2020-12-11 21:59:47 +09:00
e.initColorPalette()
e.initSysTray()
2023-01-26 01:33:47 +09:00
e.initSpecialKeys()
2020-08-01 14:22:34 +09:00
// application main window
2023-01-26 01:33:47 +09:00
isSetWindowState := e.initAppWindow()
e.window.Show()
2020-08-01 14:22:34 +09:00
// window layout
2023-01-26 01:33:47 +09:00
e.setWindowLayout()
2020-08-01 14:22:34 +09:00
// neovim workspaces
nvimErr := <-errCh
if nvimErr != nil {
fmt.Println(nvimErr)
os.Exit(1)
}
2023-01-26 01:33:47 +09:00
e.initWorkspaces(e.ctx, signal, redrawUpdates, guiUpdates, nvimCh, uiRCh, isSetWindowState)
2019-10-24 17:04:33 +09:00
2020-10-05 23:02:44 +09:00
e.connectAppSignals()
2023-08-16 23:54:23 +09:00
// go e.exitEditor(cancel, f, g)
2025-06-10 20:39:23 +09:00
// go e.exitEditor(cancel, f, fgprofStop)
2023-01-26 01:33:47 +09:00
go e.exitEditor(cancel)
2020-12-12 01:39:52 +09:00
2023-01-26 01:33:47 +09:00
e.addDockMenu()
2023-01-26 01:33:47 +09:00
widgets.QApplication_Exec()
}
2023-01-26 01:33:47 +09:00
func (e *Editor) initAppWindow() bool {
e.putLog("start preparing the application window.")
defer e.putLog("finished preparing the application window.")
e.window = frameless.CreateQFramelessWindow(frameless.FramelessConfig{
IsBorderless: e.config.Editor.BorderlessWindow,
Alpha: e.config.Editor.Transparent,
ApplyBlurEffect: e.config.Editor.EnableBackgroundBlur,
})
2023-01-26 01:33:47 +09:00
e.connectWindowEvents()
e.setWindowOptions()
e.setWindowSizeFromOpts()
2019-06-12 21:58:35 +09:00
2023-01-26 01:33:47 +09:00
return e.setInitialWindowState()
}
2019-06-12 21:58:35 +09:00
2023-01-26 01:33:47 +09:00
// exitEditor is to detect stop events and quit the application
2023-08-16 23:54:23 +09:00
// func (e *Editor) exitEditor(cancel context.CancelFunc, f, g *os.File) {
2025-06-10 20:39:23 +09:00
// func (e *Editor) exitEditor(cancel context.CancelFunc, f *os.File, fgprofStop func() error) {
2023-01-26 01:33:47 +09:00
func (e *Editor) exitEditor(cancel context.CancelFunc) {
ret := <-e.stop
close(e.stop)
e.putLog("The application was quitted with the exit of Neovim.")
if runtime.GOOS == "darwin" {
2023-01-26 01:33:47 +09:00
e.app.DisconnectEvent()
}
2023-01-26 01:33:47 +09:00
e.saveAppWindowState()
cancel()
2023-01-26 01:33:47 +09:00
// --------------------
// profile the goneovim
// --------------------
//
// Comment out the following::
2023-08-16 23:54:23 +09:00
2023-01-26 01:33:47 +09:00
// // * built-in net/http/pprof
// pprof.StopCPUProfile()
// f.Close()
2023-08-16 23:54:23 +09:00
//
// runtime.GC()
// pprof.WriteHeapProfile(g)
// g.Close()
2020-07-12 01:20:22 +09:00
2023-01-26 01:33:47 +09:00
// // * https://github.com/felixge/fgprof
// fgprofStop()
// f.Close()
os.Exit(ret)
2019-06-12 21:58:35 +09:00
}
func (e *Editor) putLog(v ...interface{}) {
2021-01-25 23:24:53 +09:00
if e.opts.Debug == "" {
return
2020-12-04 23:14:06 +09:00
}
log.Println(
fmt.Sprintf("%07.3f", float64(time.Now().UnixNano()/1000-e.startuptime)/1000),
2022-01-13 17:57:38 +09:00
strings.TrimRight(strings.TrimLeft(fmt.Sprintf("%v", v), "["), "]"),
)
2020-12-04 23:14:06 +09:00
}
2023-01-26 01:33:47 +09:00
func (e *Editor) bindResizeEvent() {
e.window.ConnectResizeEvent(func(event *gui.QResizeEvent) {
if !editor.isBindNvimSizeToAppwin {
return
}
if len(e.workspaces) == 0 {
return
}
2023-01-26 01:33:47 +09:00
e.resizeMainWindow()
})
}
2021-08-27 23:17:25 +09:00
// addDockMenu add the action menu for app in the Dock.
func (e *Editor) addDockMenu() {
if runtime.GOOS != "darwin" {
return
}
appExecutable := core.QCoreApplication_ApplicationFilePath()
menu := widgets.NewQMenu(nil)
action1 := menu.AddAction("New Instance")
action1.ConnectTriggered(func(checked bool) {
go func() {
cmd := exec.Command(appExecutable)
cmd.Start()
}()
})
action2 := menu.AddAction("New Instance with -u NONE")
action2.ConnectTriggered(func(checked bool) {
go func() {
cmd := exec.Command(appExecutable, "-u", "NONE")
cmd.Start()
}()
})
for key, string := range e.config.Editor.DockmenuActions {
action := menu.AddAction(key)
strSlice := strings.Split(string, " ")
action.ConnectTriggered(func(checked bool) {
go func() {
cmd := exec.Command(appExecutable, strSlice...)
cmd.Start()
}()
})
}
menu.SetAsDockMenu()
}
2023-11-02 22:41:48 +09:00
func parseFont(families string, size int, weight string, stretch, linespace, letterspace int) (fonts []*Font) {
weight = strings.ToLower(weight)
var fontWeight gui.QFont__Weight
switch weight {
case "thin":
fontWeight = gui.QFont__Thin
case "extralight", "ultralight":
fontWeight = gui.QFont__ExtraLight
case "light":
fontWeight = gui.QFont__Light
case "normal", "regular":
fontWeight = gui.QFont__Normal
case "demibold", "semibold":
fontWeight = gui.QFont__DemiBold
case "bold":
fontWeight = gui.QFont__Bold
case "extrabold", "ultrabold":
fontWeight = gui.QFont__ExtraBold
case "black", "heavy":
fontWeight = gui.QFont__Black
}
for _, f := range strings.Split(families, ",") {
2025-03-07 16:59:28 +09:00
font := initFontNew(strings.TrimSpace(f), float64(size), fontWeight, stretch, linespace, letterspace)
fonts = append(fonts, font)
ok := checkValidFont(f)
if !ok {
editor.fontErrors = append(editor.fontErrors, f)
continue
}
2023-11-02 22:41:48 +09:00
}
return
}
2025-03-07 16:59:28 +09:00
func (e *Editor) showFontErrors() {
if len(e.fontErrors) == 0 {
return
}
for _, fontError := range e.fontErrors {
go e.pushNotification(
NotifyWarn,
6,
2025-03-07 16:59:28 +09:00
fmt.Sprintf("The specified font family '%s' was not found on this system.", fontError),
notifyOptionArg([]*NotifyButton{}),
)
}
}
2020-08-01 14:22:34 +09:00
// setAppDirPath
2023-01-26 01:33:47 +09:00
// set application working directory path
// TODO: This process is problematic and needs a better way to set up CWD
// - https://github.com/akiyosi/goneovim/issues/43
// - https://github.com/akiyosi/goneovim/issues/337
// - https://github.com/akiyosi/goneovim/issues/325
//
2020-10-05 23:02:44 +09:00
// Set the current working directory of the application to the HOME directory in darwin, linux.
2020-08-01 14:22:34 +09:00
// If this process is not executed, CWD is set to the root directory, and
// nvim plugins called as descendants of the application will not work due to lack of permission.
// e.g. #122
2020-10-05 23:02:44 +09:00
func (e *Editor) setAppDirPath(home string) {
if runtime.GOOS == "windows" {
return
}
if os.Getenv("TERM") != "" {
return
2020-10-05 23:02:44 +09:00
}
2020-08-01 14:22:34 +09:00
path := core.QCoreApplication_ApplicationDirPath()
absHome, err := util.ExpandTildeToHomeDirectory(home)
if err == nil {
if path != absHome {
qdir := core.NewQDir2(path)
qdir.SetCurrent(absHome)
}
}
2023-01-26 01:33:47 +09:00
e.putLog("set working directory")
}
func (e *Editor) overwriteConfigByCLIOption() {
e.config.Editor.ExtTabline = e.opts.Exttabline || e.config.Editor.ExtTabline
e.config.Editor.ExtCmdline = e.opts.Extcmdline || e.config.Editor.ExtCmdline
e.config.Editor.ExtPopupmenu = e.opts.Extpopupmenu || e.config.Editor.ExtPopupmenu
e.config.Editor.ExtMessages = e.opts.Extmessages || e.config.Editor.ExtMessages
e.config.Editor.ExtCmdline = e.opts.Extmessages || e.config.Editor.ExtCmdline
e.config.Editor.StartFullscreen = e.opts.Fullscreen || e.config.Editor.StartFullscreen
e.config.Editor.StartMaximizedWindow = e.opts.Maximized || e.config.Editor.StartMaximizedWindow
2020-08-01 14:22:34 +09:00
}
2019-10-20 18:13:56 +09:00
func (e *Editor) newSplitter() {
splitter := widgets.NewQSplitter2(core.Qt__Horizontal, nil)
splitter.SetStyleSheet("* {background-color: rgba(0, 0, 0, 0);}")
splitter.SetStretchFactor(1, 100)
splitter.SetObjectName("splitter")
2020-08-28 00:54:52 +09:00
e.splitter = splitter
2019-10-20 18:13:56 +09:00
}
2023-01-26 01:33:47 +09:00
func (e *Editor) setDebuglog() (file *os.File) {
if e.opts.Debug == "" {
return nil
}
file, err := os.OpenFile(e.opts.Debug, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Println(err)
2023-01-26 01:33:47 +09:00
os.Exit(1)
}
2023-01-26 01:33:47 +09:00
log.SetOutput(file)
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
return
}
2023-04-21 00:08:05 +09:00
func (e *Editor) initWorkspaces(ctx context.Context, signal *neovimSignal, redrawUpdates chan [][]interface{}, guiUpdates chan []interface{}, nvimCh chan *nvim.Nvim, uiRemoteAttachedCh chan bool, isSetWindowState bool) {
2023-01-26 01:33:47 +09:00
e.workspaces = []*Workspace{}
// ws := newWorkspace()
// ws.registerSignal(signal, &redrawUpdates, &guiUpdates)
// ws.initUI()
// ws.updateSize()
// go ws.bindNvim(nvimCh, uiRCh, isSetWindowState)
// e.workspaces = append(e.workspaces, ws)
// ws.widget.SetParent(e.widget)
editor.putLog("start initializing workspaces")
2023-01-26 01:33:47 +09:00
// Detect session file
sessionExists := false
restoreFiles := []string{}
for i := 0; i <= WORKSPACELEN; i++ {
path := filepath.Join(e.configDir, "sessions", strconv.Itoa(i)+".vim")
_, err := os.Stat(path)
if err != nil {
continue
2018-10-11 21:37:36 +09:00
}
2023-01-26 01:33:47 +09:00
sessionExists = true
restoreFiles = append(restoreFiles, path)
2018-10-11 21:37:36 +09:00
}
2023-01-26 01:33:47 +09:00
e.doRestoreSessions = sessionExists && e.config.Workspace.RestoreSession
if len(restoreFiles) == 0 || !e.doRestoreSessions {
restoreFiles = []string{""}
}
editor.putLog("done checking sessions")
2023-01-26 01:33:47 +09:00
for i, file := range restoreFiles {
ws := newWorkspace()
ws.initUI()
if i == 0 {
2023-11-02 22:41:48 +09:00
fonts := <-e.fontCh
e.font = fonts[0]
if len(fonts) > 1 {
e.fallbackfonts = fonts[1:]
}
}
ws.initFont()
e.initAppFont()
ws.registerSignal(signal, redrawUpdates, guiUpdates)
2023-01-26 01:33:47 +09:00
ws.updateSize()
// Only the first nvim instance is lazy-bound to the workspace,
// but the second and subsequent instances are not lazy-bound
isLazyBind := true
if i > 0 {
isLazyBind = false
signal, redrawUpdates, guiUpdates, nvimCh, uiRemoteAttachedCh, _ = newNvim(ws.cols, ws.rows, ctx)
2018-10-11 21:37:36 +09:00
}
2023-01-26 01:33:47 +09:00
2018-10-11 21:37:36 +09:00
e.workspaces = append(e.workspaces, ws)
go ws.bindNvim(nvimCh, uiRemoteAttachedCh, isSetWindowState, isLazyBind, file)
2018-10-11 21:37:36 +09:00
}
2023-01-26 01:33:47 +09:00
e.putLog("done initialazing workspaces")
2019-06-12 21:58:35 +09:00
}
2018-10-11 21:37:36 +09:00
2020-10-05 23:02:44 +09:00
func (e *Editor) connectAppSignals() {
if e.app == nil {
return
}
2023-01-26 01:33:47 +09:00
if runtime.GOOS == "darwin" {
if e.openingFileCh == nil {
e.openingFileCh = make(chan string, 2)
}
go func() {
for {
openingFile := <-e.openingFileCh
if strings.Join(editor.args, "") != "" {
continue
}
e.loadFileInDarwin(
openingFile,
)
}
}()
2023-01-26 01:33:47 +09:00
e.app.ConnectEvent(func(event *core.QEvent) bool {
switch event.Type() {
case core.QEvent__FileOpen:
// If goneovim not launched on finder (it is started in terminal)
if os.Getenv("TERM") != "" {
2023-01-26 01:33:47 +09:00
return false
}
fileOpenEvent := gui.NewQFileOpenEventFromPointer(event.Pointer())
e.loadFileInDarwin(
fileOpenEvent.File(),
)
2019-06-12 21:58:35 +09:00
}
2023-01-26 01:33:47 +09:00
return true
})
}
e.signal.ConnectSidebarSignal(func() {
if e.side != nil {
return
2018-05-25 01:00:23 +09:00
}
2023-01-26 01:33:47 +09:00
e.putLog("create workspace sidebar")
e.side = newWorkspaceSide()
e.side.newScrollArea()
e.side.scrollarea.Hide()
e.side.scrollarea.SetWidget(e.side.widget)
e.splitter.InsertWidget(0, e.side.scrollarea)
side := e.side
if e.config.SideBar.Visible {
side.show()
}
})
go e.toEmmitGeometrySignal()
go e.signal.ConnectGeometrySignal(func() {
e.AdjustSizeBasedOnFontmetrics(e.windowSize[0], e.windowSize[1])
})
2023-01-26 01:33:47 +09:00
// When an application is closed with the Close button
e.window.ConnectCloseEvent(func(event *gui.QCloseEvent) {
e.putLog("The application was closed outside of Neovim's commands, such as the Close button.")
// A request to exit the application via the close button has been issued,
// intercept this request and send quit command to the nvim process.
event.Ignore()
if e.config.Workspace.RestoreSession {
e.cleanup()
e.saveSessions()
}
var cmd string
if e.config.Editor.IgnoreSaveConfirmationWithCloseButton {
cmd = "qa!"
} else {
cmd = "confirm qa"
}
for _, ws := range e.workspaces {
go ws.nvim.Command(cmd)
}
return
2019-06-12 21:58:35 +09:00
})
2023-01-26 01:33:47 +09:00
e.putLog("done connecting UI siganal")
2020-10-05 23:02:44 +09:00
}
func (e *Editor) toEmmitGeometrySignal() {
for {
time.Sleep(100 * time.Millisecond)
e.geometryUpdateMutex.RLock()
if e.geometryUpdateTimer == nil {
e.geometryUpdateMutex.RUnlock()
continue
}
select {
case <-e.geometryUpdateTimer.C:
e.geometryUpdateMutex.RUnlock()
e.signal.GeometrySignal()
default:
e.geometryUpdateMutex.RUnlock()
continue
}
}
}
func (e *Editor) resizeMainWindow() {
cws := e.workspaces[e.active]
2023-01-26 01:33:47 +09:00
windowWidth, windowHeight, _, _ := cws.updateSize()
e.windowSize = [2]int{windowWidth, windowHeight}
e.relocateNotifications()
if !editor.config.Editor.WindowGeometryBasedOnFontmetrics {
return
}
e.geometryUpdateMutex.Lock()
if e.geometryUpdateTimer == nil {
e.geometryUpdateTimer = time.NewTimer(200 * time.Millisecond)
} else {
if !e.geometryUpdateTimer.Stop() {
select {
case <-e.geometryUpdateTimer.C:
default:
}
}
e.geometryUpdateTimer.Reset(200 * time.Millisecond)
}
e.geometryUpdateMutex.Unlock()
}
func (e *Editor) AdjustSizeBasedOnFontmetrics(windowWidth, windowHeight int) {
if e.window.WindowState() == core.Qt__WindowFullScreen || e.isWindowMaximizing {
return
}
// quantization of window geometry with font metrics as the smallest unit of change.
geometry := editor.window.Geometry()
width := geometry.Width()
height := geometry.Height()
if !(width == windowWidth && height == windowHeight) {
e.window.Resize2(
windowWidth,
windowHeight,
)
}
}
func (e *Editor) loadFileInDarwin(file string) {
2020-10-05 23:02:44 +09:00
if runtime.GOOS != "darwin" {
return
}
goneovim := e.workspaces[e.active].nvim
isModified := ""
isModified, _ = goneovim.CommandOutput("echo &modified")
if isModified == "1" {
goneovim.Command(fmt.Sprintf(":tabe %s", file))
2020-10-05 23:02:44 +09:00
} else {
goneovim.Command(fmt.Sprintf("%s %s", e.config.Editor.FileOpenCmd, file))
2020-10-05 23:02:44 +09:00
}
2018-01-05 08:07:29 +00:00
}
2018-12-29 17:31:44 +09:00
func (e *Editor) initNotifications() {
e.notifications = []*Notification{}
2019-06-12 21:58:35 +09:00
e.notificationWidth = e.config.Editor.Width * 2 / 3
2018-12-29 17:31:44 +09:00
e.notifyStartPos = core.NewQPoint2(e.width-e.notificationWidth-10, e.height-30)
e.signal.ConnectNotifySignal(func() {
notify := <-e.notify
if notify.message == "" {
return
}
if notify.buttons == nil {
e.popupNotification(notify.level, notify.period, notify.message)
} else {
e.popupNotification(notify.level, notify.period, notify.message, notifyOptionArg(notify.buttons))
}
})
2023-01-26 01:33:47 +09:00
e.putLog("initializing notification UI")
2018-12-29 17:31:44 +09:00
}
2019-07-11 12:18:17 +09:00
func (e *Editor) initSysTray() {
if !e.config.Editor.DesktopNotifications {
return
}
2019-07-11 18:28:37 +09:00
pixmap := gui.NewQPixmap()
2019-07-12 00:28:27 +09:00
color := ""
size := 0.95
if runtime.GOOS == "darwin" {
2024-11-04 11:07:45 +09:00
if isDarkMode() {
color = "#ffffff"
} else {
color = "#434343"
}
2019-07-12 00:28:27 +09:00
size = 0.9
} else {
2024-11-04 11:07:45 +09:00
if isDarkMode() {
color = "#ffffff"
} else {
color = "#179A33"
}
2019-07-12 00:28:27 +09:00
}
2024-11-04 11:07:45 +09:00
2019-07-12 00:28:27 +09:00
svg := fmt.Sprintf(`<svg viewBox="0 0 128 128"><g transform="translate(2,3) scale(%f)"><path fill="%s" d="M72.6 80.5c.2.2.6.5.9.5h5.3c.3 0 .7-.3.9-.5l1.4-1.5c.2-.2.3-.4.3-.6l1.5-5.1c.1-.5 0-1-.3-1.3l-1.1-.9c-.2-.2-.6-.1-.9-.1h-4.8l-.2-.2-.1-.1c-.2 0-.4-.1-.6.1l-1.9 1.2c-.2 0-.3.5-.4.7l-1.6 4.9c-.2.5-.1 1.1.3 1.5l1.3 1.4zM73.4 106.9l-.4.1h-1.2l7.2-21.1c.2-.7-.1-1.5-.8-1.7l-.4-.1h-12.1c-.5.1-.9.5-1 1l-.7 2.5c-.2.7.3 1.3 1 1.5l.3-.1h1.8l-7.3 20.9c-.2.7.1 1.6.8 1.9l.4.3h11.2c.6 0 1.1-.5 1.3-1.1l.7-2.4c.3-.7-.1-1.5-.8-1.7zM126.5 87.2l-1.9-2.5v-.1c-.3-.3-.6-.6-1-.6h-7.2c-.4 0-.7.4-1 .6l-2 2.4h-3.1l-2.1-2.4v-.1c-.2-.3-.6-.5-1-.5h-4l20.2-20.2-22.6-22.4 20.2-20.8v-9l-2.8-3.6h-40.9l-3.3 3.5v2.9l-11.3-11.4-7.7 7.5-2.4-2.5h-40.4l-3.2 3.7v9.4l3 2.9h3v26.1l-14 14 14 14v32l5.2 2.9h11.6l9.1-9.5 21.6 21.6 14.5-14.5c.1.4.4.5.9.7l.4-.2h9.4c.6 0 1.1-.1 1.2-.6l.7-2c.2-.7-.1-1.3-.8-1.5l-.4.1h-.4l3.4-10.7 2.3-2.3h5l-5 15.9c-.2.7.2 1.1.9 1.4l.4-.2h9.1c.5 0 1-.1 1.2-.6l.8-1.8c.3-.7-.1-1.3-.7-1.6-.1-.1-.3 0-.5 0h-.4l4.2-13h6.1l-5.1 15.9c-.2.7.2 1.1.9 1.3l.4-.3h10c.5 0 1-.1 1.2-.6l.8-2c.3-.7-.1-1.3-.8-1.5-.1-.1-.3.1-.5.1h-.7l5.6-18.5c.2-.5.1-1.1-.1-1.4zm-63.8-82.3l11.3 11.3v4.7l3.4 4.1h1.6l-29 28v-28h3.3l2.7-4.2v-8.9l-.2-.3 6.9-6.7zm-59.8 59.2l12.1-12.1v24.2l-12.1-12.1zm38.9 38.3l58.4-60 21.4 21.5-20.2 20.2h-.1c-.3.1-.5.3-.7.5l-2.1 2.4h-2.9l-2.2-2.4c-.2-.3-.6-.6-1-.6h-8.8c-.6 0-1.1.4-1.3 1l-.8 2.5c-.2.7.1 1.3.8 1.6h1.5l-6.4 18.9-15.1 15.2-20.5-20.8z"></path></g></svg>`, size, color)
2019-07-11 18:28:37 +09:00
pixmap.LoadFromData2(core.NewQByteArray2(svg, len(svg)), "SVG", core.Qt__ColorOnly)
trayIcon := gui.NewQIcon2(pixmap)
image := filepath.Join(e.configDir, "trayicon.png")
if isFileExist(image) {
trayIcon = gui.NewQIcon5(image)
}
2019-07-11 12:18:17 +09:00
e.sysTray = widgets.NewQSystemTrayIcon2(trayIcon, e.app)
2019-07-11 15:47:36 +09:00
e.sysTray.Show()
e.putLog("initialize system tray")
2019-07-11 12:18:17 +09:00
}
2024-11-04 11:07:45 +09:00
func isDarkMode() bool {
plt := gui.NewQPalette()
txtColor := plt.Color2(gui.QPalette__WindowText)
winColor := plt.Color2(gui.QPalette__Window)
return txtColor.LightnessF() > winColor.LightnessF()
}
2023-01-26 01:33:47 +09:00
func (e *Editor) setEnvironmentVariables() {
// For Linux
2019-06-12 21:58:35 +09:00
if runtime.GOOS == "linux" {
// // It was not a necessary process to export the following environment variables.
// exe, _ := os.Executable()
// dir, _ := filepath.Split(exe)
// _ = os.Setenv("LD_LIBRARY_PATH", dir+"lib")
// _ = os.Setenv("QT_PLUGIN_PATH", dir+"plugins")
// _ = os.Setenv("RESOURCE_NAME", "goneovim")
2019-06-12 21:58:35 +09:00
}
// If the OS is MacOS and the application is launched from an .app
if runtime.GOOS == "darwin" && os.Getenv("TERM") == "" {
2019-12-07 13:19:58 +09:00
shell := os.Getenv("SHELL")
if shell == "" {
shell = "/bin/zsh" // fallback
2019-12-07 13:19:58 +09:00
}
cmd := exec.Command(shell, "-l", "-c", "env")
2019-12-07 13:19:58 +09:00
stdout, err := cmd.StdoutPipe()
if err != nil {
2021-02-01 22:30:09 +09:00
e.putLog(err)
2019-12-07 13:19:58 +09:00
return
}
if err := cmd.Start(); err != nil {
2021-02-01 22:30:09 +09:00
e.putLog(err)
2019-12-07 13:19:58 +09:00
return
}
output, err := io.ReadAll(stdout)
stdout.Close()
2019-12-07 13:19:58 +09:00
if err != nil {
2021-02-01 22:30:09 +09:00
e.putLog(err)
2019-12-07 13:19:58 +09:00
return
}
for _, line := range strings.Split(string(output), "\n") {
splits := strings.SplitN(line, "=", 2)
if len(splits) == 2 {
2019-12-07 13:19:58 +09:00
_ = os.Setenv(splits[0], splits[1])
}
}
}
// For Windows
// https://github.com/equalsraf/neovim-qt/issues/391
if runtime.GOOS == "windows" {
_ = os.Setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1")
}
2023-01-26 01:33:47 +09:00
e.putLog("setting environment variable")
2019-06-12 21:58:35 +09:00
}
2023-01-26 01:33:47 +09:00
func (e *Editor) initAppFont() {
e.app.SetFont(e.font.qfont, "QWidget")
e.app.SetFont(e.font.qfont, "QLabel")
2023-01-26 01:33:47 +09:00
e.putLog("initializing font")
2019-06-12 21:58:35 +09:00
}
2022-05-27 20:55:49 +09:00
// pushNotification is
//
// level: notify level
// period: display period
func (e *Editor) pushNotification(level NotifyLevel, p int, message string, opt ...NotifyOptionArg) {
a := NotifyOptions{}
2018-08-16 19:31:36 +09:00
for _, o := range opt {
o(&a)
2018-08-16 19:31:36 +09:00
}
n := &Notify{
level: level,
period: p,
2018-08-16 19:31:36 +09:00
message: message,
buttons: a.buttons,
2018-08-16 19:31:36 +09:00
}
e.notify <- n
e.signal.NotifySignal()
}
func (e *Editor) popupNotification(level NotifyLevel, p int, message string, opt ...NotifyOptionArg) {
2020-08-10 23:19:00 +09:00
e.updateNotificationPos()
notification := newNotification(level, p, message, opt...)
2018-08-15 17:44:53 +09:00
notification.widget.SetParent(e.window)
notification.widget.AdjustSize()
x := e.notifyStartPos.X()
y := e.notifyStartPos.Y() - notification.widget.Height() - 4
2018-08-15 17:44:53 +09:00
notification.widget.Move2(x, y)
e.notifyStartPos = core.NewQPoint2(x, y)
e.notifications = append(e.notifications, notification)
2018-08-15 17:44:53 +09:00
notification.show()
}
2019-06-12 21:58:35 +09:00
func (e *Editor) initColorPalette() {
rgbAccent := hexToRGBA(e.config.SideBar.AccentColor)
2018-12-28 01:40:33 +09:00
fg := newRGBA(180, 185, 190, 1)
bg := newRGBA(9, 13, 17, 1)
2019-06-12 21:58:35 +09:00
c := &ColorPalette{
e: e,
2018-12-31 16:43:04 +09:00
bg: bg,
fg: fg,
2018-12-28 15:46:52 +09:00
selectedBg: bg.brend(rgbAccent, 0.3),
2018-12-31 16:43:04 +09:00
matchFg: rgbAccent,
2018-12-27 12:38:42 +09:00
}
2019-06-12 21:58:35 +09:00
e.colors = c
e.colors.update()
2023-01-26 01:33:47 +09:00
e.putLog("initializing color palette")
2018-12-27 12:38:42 +09:00
}
func parseLinesAndColumns(args []string) (int, error, int, error) {
var columns, lines int = -1, -1
pattern := regexp.MustCompile(`lines=(\d+)|columns=(\d+)|vim\.o\["(lines|columns)"\]=(\d+)`)
for _, arg := range args {
matches := pattern.FindAllStringSubmatch(arg, -1)
for _, match := range matches {
if match[1] != "" { // "lines=XX"
if val, err := strconv.Atoi(match[1]); err == nil {
lines = val
}
} else if match[2] != "" { // "columns=XX"
if val, err := strconv.Atoi(match[2]); err == nil {
columns = val
}
} else if match[3] == "lines" && match[4] != "" { // vim.o["lines"]=XX
if val, err := strconv.Atoi(match[4]); err == nil {
lines = val
}
} else if match[3] == "columns" && match[4] != "" { // vim.o["columns"]=XX
if val, err := strconv.Atoi(match[4]); err == nil {
columns = val
}
}
}
}
if columns == -1 && lines == -1 {
return 100, fmt.Errorf("columns are not set"), 50, fmt.Errorf("lines are not set")
}
var cerr error
if columns == -1 {
columns = 100
cerr = fmt.Errorf("columns are not set")
}
var lerr error
if lines == -1 {
lines = 50
lerr = fmt.Errorf("lines are not set")
}
return columns, cerr, lines, lerr
}
2018-12-27 21:03:21 +09:00
func (c *ColorPalette) update() {
fg := c.fg
bg := c.bg
c.selectedBg = bg.brend(hexToRGBA(c.e.config.SideBar.AccentColor), 0.3)
2019-03-20 22:41:06 +09:00
c.inactiveFg = warpColor(bg, -80)
c.comment = warpColor(fg, -80)
2018-12-28 01:40:33 +09:00
c.abyss = warpColor(bg, 5)
2019-03-20 22:41:06 +09:00
c.sideBarFg = warpColor(fg, -5)
c.sideBarBg = warpColor(bg, -5)
c.sideBarSelectedItemBg = warpColor(bg, -15)
c.scrollBarFg = warpColor(bg, -20)
2018-12-27 21:03:21 +09:00
c.scrollBarBg = bg
2019-03-20 22:41:06 +09:00
c.widgetFg = warpColor(fg, 5)
c.widgetBg = warpColor(bg, -10)
c.widgetInputArea = warpColor(bg, -30)
c.minimapCurrentRegion = warpColor(bg, 20)
if c.e.config.Editor.WindowSeparatorColor != "" {
c.windowSeparator = hexToRGBA(c.e.config.Editor.WindowSeparatorColor)
} else if c.e.config.Editor.WindowSeparatorTheme == "light" && c.e.config.Editor.WindowSeparatorColor == "" {
if fg.R < 250 && fg.G < 250 && fg.B < 250 {
c.windowSeparator = warpColor(fg, 10)
} else {
c.windowSeparator = warpColor(fg, -10)
}
} else if c.e.config.Editor.WindowSeparatorTheme == "dark" && c.e.config.Editor.WindowSeparatorColor == "" {
if bg.R > 10 && bg.G > 10 && bg.B > 10 {
c.windowSeparator = warpColor(bg, 10)
} else {
c.windowSeparator = warpColor(bg, -10)
}
}
2019-10-29 21:53:26 +09:00
c.indentGuide = warpColor(bg, -30)
2018-12-27 21:03:21 +09:00
}
func (e *Editor) updateGUIColor() {
2023-01-26 01:33:47 +09:00
e.putLog("start updating GUI color")
2019-01-01 18:34:51 +09:00
e.workspaces[e.active].updateWorkspaceColor()
2019-03-02 21:40:40 +09:00
2019-10-27 15:59:27 +09:00
// Do not use frameless drawing on linux
if runtime.GOOS == "linux" {
2020-12-12 01:39:52 +09:00
e.window.TitleBar.Hide()
e.window.WindowWidget.SetStyleSheet(fmt.Sprintf(" #QFramelessWidget { background-color: rgba(%d, %d, %d, %f); border-radius: 0px;}", e.colors.bg.R, e.colors.bg.G, e.colors.bg.B, e.config.Editor.Transparent))
e.window.SetWindowFlag(core.Qt__FramelessWindowHint, false)
e.window.SetWindowFlag(core.Qt__NoDropShadowWindowHint, false)
e.window.Show()
2019-10-27 15:59:27 +09:00
} else {
2020-12-12 01:39:52 +09:00
e.window.SetupWidgetColor((uint16)(e.colors.bg.R), (uint16)(e.colors.bg.G), (uint16)(e.colors.bg.B))
e.window.SetupTitleColor((uint16)(e.colors.fg.R), (uint16)(e.colors.fg.G), (uint16)(e.colors.fg.B))
2019-05-09 23:22:43 +09:00
}
2020-08-10 23:19:00 +09:00
// e.window.SetWindowOpacity(1.0)
2023-01-26 01:33:47 +09:00
e.putLog("finished updating GUI color")
2018-12-27 21:03:21 +09:00
}
2018-06-24 13:36:41 +09:00
func hexToRGBA(hex string) *RGBA {
format := "#%02x%02x%02x"
if len(hex) == 4 {
format = "#%1x%1x%1x"
}
var r, g, b uint8
n, err := fmt.Sscanf(hex, format, &r, &g, &b)
if err != nil {
return nil
}
if n != 3 {
return nil
}
rgba := &RGBA{
R: (int)(r),
G: (int)(g),
B: (int)(b),
A: 1,
}
return rgba
}
func (e *Editor) setWindowSizeFromOpts() {
2023-01-26 01:33:47 +09:00
e.window.SetupBorderSize(e.config.Editor.Margin)
e.window.SetupWindowGap(e.config.Editor.Gap)
if e.opts.Geometry != "" {
width, height := e.setWindowSize(e.opts.Geometry)
e.config.Editor.Width = width
e.config.Editor.Height = height
}
}
func (e *Editor) setWindowSize(s string) (int, int) {
var width, height int
var err error
parsed_s := strings.SplitN(s, "x", 2)
if len(parsed_s) != 2 {
// TODO: Error message to user?
return 40, 30
}
width, err = strconv.Atoi(parsed_s[0])
if err != nil || width < 40 {
width = 40
}
height, err = strconv.Atoi(parsed_s[1])
if err != nil || height < 30 {
height = 30
}
return width, height
}
2023-01-26 01:33:47 +09:00
func (e *Editor) restoreWindow() {
if !e.config.Editor.RestoreWindowGeometry {
return
}
settings := core.NewQSettings("neovim", "goneovim", nil)
geometry := settings.Value("geometry", core.NewQVariant13(core.NewQByteArray()))
geometryBA := geometry.ToByteArray()
if geometryBA.Length() != 0 {
e.window.RestoreGeometry(geometryBA)
}
// Restoring `windowState` causes problems when the previous session did
// something like “maximize → manual resize.” The maximize flag survives,
// so on the next launch the window opens maximized and the original
// geometry is lost. Therefore, we restore only the geometry and skip
// `windowState`.
// state := settings.Value("windowState", core.NewQVariant13(core.NewQByteArray()))
// stateBA := state.ToByteArray()
// if stateBA.Length() != 0 {
// e.window.RestoreFramelessState(stateBA, 0)
// // isRestoreState = true
// }
return
}
func (e *Editor) connectWindowEvents() {
e.window.ConnectKeyPressEvent(e.keyPress)
e.window.ConnectKeyReleaseEvent(e.keyRelease)
e.bindResizeEvent()
2023-01-26 01:33:47 +09:00
e.window.ConnectShowEvent(func(event *gui.QShowEvent) {
editor.putLog("show application window")
})
e.window.InstallEventFilter(e.window)
e.window.ConnectEventFilter(func(watched *core.QObject, event *core.QEvent) bool {
switch event.Type() {
case core.QEvent__ActivationChange:
if e.window.IsActiveWindow() {
e.isWindowNowActivated = true
e.isWindowNowInactivated = false
} else if !e.window.IsActiveWindow() {
e.isWindowNowActivated = false
e.isWindowNowInactivated = true
}
case core.QEvent__WindowStateChange:
if e.window.WindowState() == core.Qt__WindowMaximized {
if !e.isWindowMaximizing {
e.isWindowMaximizing = true
if editor.config.Editor.WindowGeometryBasedOnFontmetrics {
go e.window.WindowMaximize()
}
}
} else {
e.isWindowMaximizing = false
}
default:
}
return e.window.QFramelessDefaultEventFilter(watched, event)
})
}
2018-12-29 15:40:57 +09:00
func (e *Editor) setWindowOptions() {
// e.window.SetupTitle("Neovim")
e.window.SetMinimumSize2(40, 30)
2020-03-22 22:51:13 +09:00
e.window.SetAttribute(core.Qt__WA_KeyCompression, false)
2018-12-29 15:40:57 +09:00
e.window.SetAcceptDrops(true)
}
2023-01-26 01:33:47 +09:00
func (e *Editor) setInitialWindowState() (isSetWindowState bool) {
2020-08-12 00:02:45 +09:00
e.width = e.config.Editor.Width
e.height = e.config.Editor.Height
if e.config.Editor.HideTitlebar {
e.window.IsTitlebarHidden = true
e.window.TitleBar.Hide()
}
2023-01-26 01:33:47 +09:00
// isRestoreGeometry, isRestoreState := e.restoreWindow()
// If command line options are given, they take priority.
2023-01-26 01:33:47 +09:00
if e.config.Editor.StartFullscreen ||
e.config.Editor.StartMaximizedWindow {
if e.config.Editor.StartFullscreen {
e.window.WindowFullScreen()
2023-01-26 01:33:47 +09:00
} else if e.config.Editor.StartMaximizedWindow {
e.window.WindowMaximize()
}
2023-01-26 01:33:47 +09:00
isSetWindowState = true
} else {
2023-01-26 01:33:47 +09:00
if e.config.Editor.RestoreWindowGeometry && e.opts.Geometry == "" {
e.restoreWindow()
} else {
e.window.Resize2(e.width, e.height)
}
}
2023-01-26 01:33:47 +09:00
return
}
func (e *Editor) setWindowLayout() {
// window layout
l := widgets.NewQBoxLayout(widgets.QBoxLayout__RightToLeft, nil)
l.SetContentsMargins(0, 0, 0, 0)
l.SetSpacing(0)
e.window.SetupContent(l)
// window content
e.widget = widgets.NewQWidget(nil, 0)
e.newSplitter()
e.splitter.InsertWidget(1, e.widget)
l.AddWidget(e.splitter, 1, 0)
e.putLog("window content layout done")
2020-08-12 00:02:45 +09:00
}
2018-04-19 21:07:48 +09:00
func isFileExist(filename string) bool {
2018-04-30 16:58:26 +09:00
_, err := os.Stat(filename)
return err == nil
2018-04-19 21:07:48 +09:00
}
2023-01-26 01:33:47 +09:00
func (e *Editor) workspaceAdd() {
2021-05-13 23:52:40 +09:00
if len(e.workspaces) == WORKSPACELEN {
return
}
2018-12-28 13:53:55 +09:00
editor.isSetGuiColor = false
2023-01-26 01:33:47 +09:00
ws := newWorkspace()
ws.initUI()
ws.initFont()
2023-01-26 01:33:47 +09:00
ws.updateSize()
signal, redrawUpdates, guiUpdates, nvimCh, uiRemoteAttachedCh, _ := newNvim(ws.cols, ws.rows, e.ctx)
2023-01-26 01:33:47 +09:00
ws.registerSignal(signal, redrawUpdates, guiUpdates)
2018-05-20 12:06:29 +09:00
e.workspaces = append(e.workspaces, ws)
2018-05-01 19:50:47 +09:00
e.active = len(e.workspaces) - 1
2018-01-05 09:13:50 +00:00
e.workspaces[e.active] = ws
ws.bindNvim(nvimCh, uiRemoteAttachedCh, false, false, "")
e.workspaceUpdate()
2018-01-08 10:23:16 +00:00
}
func (e *Editor) workspaceSwitch(index int) {
index--
if index < 0 || index >= len(e.workspaces) {
return
}
e.active = index
2018-01-05 09:13:50 +00:00
e.workspaceUpdate()
}
func (e *Editor) workspaceNext() {
e.active++
if e.active >= len(e.workspaces) {
e.active = 0
}
e.workspaceUpdate()
}
2018-01-11 06:11:34 +00:00
func (e *Editor) workspacePrevious() {
e.active--
if e.active < 0 {
e.active = len(e.workspaces) - 1
}
e.workspaceUpdate()
}
2018-01-05 09:13:50 +00:00
func (e *Editor) workspaceUpdate() {
if e.side == nil {
return
}
2018-01-05 09:13:50 +00:00
for i, ws := range e.workspaces {
if i == e.active {
2021-05-05 10:31:34 +09:00
ws.hide()
2018-01-05 09:13:50 +00:00
ws.show()
} else {
ws.hide()
}
}
for i := 0; i < len(e.side.items) && i < len(e.workspaces); i++ {
2021-05-08 01:58:26 +09:00
if e.side.items[i] == nil {
continue
}
2021-05-08 08:42:19 +09:00
if e.side.items[i].label.Text() == "" {
e.workspaces[i].setCwd(e.workspaces[i].cwdlabel)
}
e.side.items[i].setSideItemLabel(i)
2021-05-05 10:31:34 +09:00
// e.side.items[i].setText(e.workspaces[i].cwdlabel)
e.side.items[i].show()
2018-01-08 10:23:16 +00:00
}
for i := len(e.workspaces); i < len(e.side.items); i++ {
2021-05-08 01:58:26 +09:00
if e.side.items[i] == nil {
continue
}
e.side.items[i].hide()
2018-01-08 10:23:16 +00:00
}
2018-01-05 09:13:50 +00:00
}
2022-03-01 14:30:47 +09:00
func (e *Editor) close(exitcode int) {
2018-01-08 10:23:16 +00:00
e.stopOnce.Do(func() {
2022-03-01 14:30:47 +09:00
e.stop <- exitcode
2018-01-08 10:23:16 +00:00
})
}
func (e *Editor) saveAppWindowState() {
2023-01-26 01:33:47 +09:00
e.putLog("save application window state")
settings := core.NewQSettings("neovim", "goneovim", nil)
settings.SetValue("geometry", core.NewQVariant13(e.window.SaveGeometry()))
settings.SetValue("windowState", core.NewQVariant13(e.window.SaveState(0)))
}
2018-01-08 10:23:16 +00:00
func (e *Editor) cleanup() {
2022-06-13 21:08:53 +09:00
// TODO We need to kill the minimap nvim process explicitly?
if !e.config.Workspace.RestoreSession {
return
}
sessions := filepath.Join(e.configDir, "sessions")
2018-01-08 10:23:16 +00:00
os.RemoveAll(sessions)
os.MkdirAll(sessions, 0755)
}
2018-01-11 06:11:34 +00:00
func (e *Editor) saveSessions() {
if !e.config.Workspace.RestoreSession {
return
}
2018-01-11 06:11:34 +00:00
sessions := filepath.Join(e.configDir, "sessions")
2018-01-08 10:23:16 +00:00
for i, ws := range e.workspaces {
2023-01-26 01:33:47 +09:00
if ws.uiRemoteAttached {
continue
}
2018-01-08 10:23:16 +00:00
sessionPath := filepath.Join(sessions, strconv.Itoa(i)+".vim")
2023-01-26 01:33:47 +09:00
ws.nvim.Command("mksession " + sessionPath)
2018-01-08 10:23:16 +00:00
}
}