mirror of
https://github.com/akiyosi/goneovim.git
synced 2025-08-05 02:24:44 +02:00
3234 lines
74 KiB
Go
3234 lines
74 KiB
Go
package editor
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/akiyosi/goneovim/filer"
|
|
"github.com/akiyosi/goneovim/util"
|
|
"github.com/akiyosi/qt/core"
|
|
"github.com/akiyosi/qt/gui"
|
|
"github.com/akiyosi/qt/svg"
|
|
"github.com/akiyosi/qt/widgets"
|
|
shortpath "github.com/akiyosi/short_path"
|
|
"github.com/neovim/go-client/nvim"
|
|
)
|
|
|
|
type neovimSignal struct {
|
|
core.QObject
|
|
|
|
_ func() `signal:"stopSignal"`
|
|
_ func() `signal:"redrawSignal"`
|
|
_ func() `signal:"guiSignal"`
|
|
|
|
_ func() `signal:"messageSignal"`
|
|
|
|
_ func() `signal:"lazyLoadSignal"`
|
|
}
|
|
|
|
type ShouldUpdate struct {
|
|
minimap bool
|
|
cursor bool
|
|
globalgrid bool
|
|
}
|
|
|
|
// Workspace is an editor workspace
|
|
type Workspace struct {
|
|
shouldUpdate *ShouldUpdate
|
|
foreground *RGBA
|
|
layout2 *widgets.QHBoxLayout
|
|
stop chan struct{}
|
|
font *Font
|
|
cursor *Cursor
|
|
tabline *Tabline
|
|
screen *Screen
|
|
scrollBar *ScrollBar
|
|
palette *Palette
|
|
popup *PopupMenu
|
|
cmdline *Cmdline
|
|
message *Message
|
|
minimap *MiniMap
|
|
fontdialog *widgets.QFontDialog
|
|
guiUpdates chan []interface{}
|
|
redrawUpdates chan [][]interface{}
|
|
flushCh chan []interface{}
|
|
signal *neovimSignal
|
|
nvim *nvim.Nvim
|
|
widget *widgets.QWidget
|
|
special *RGBA
|
|
background *RGBA
|
|
colorscheme string
|
|
cwdlabel string
|
|
escKeyInNormal string
|
|
mode string
|
|
cwdBase string
|
|
cwd string
|
|
escKeyInInsert string
|
|
filepath string
|
|
screenbg string
|
|
mouseScroll string
|
|
mouseScrollTemp string
|
|
normalMappings []*nvim.Mapping
|
|
modeInfo []map[string]interface{}
|
|
insertMappings []*nvim.Mapping
|
|
viewport [5]int
|
|
oldViewport [5]int
|
|
height int
|
|
maxLineDelta int
|
|
maxLine int
|
|
rows int
|
|
cols int
|
|
showtabline int
|
|
width int
|
|
modeIdx int
|
|
pb int
|
|
ts int
|
|
ph int
|
|
winbar *string
|
|
optionsetMutex sync.RWMutex
|
|
viewportMutex sync.RWMutex
|
|
stopOnce sync.Once
|
|
fontMutex sync.Mutex
|
|
hidden bool
|
|
uiAttached bool
|
|
uiRemoteAttached bool
|
|
isMappingScrollKey bool
|
|
hasLazyUI bool
|
|
cursorStyleEnabled bool
|
|
isDrawTabline bool
|
|
isMouseEnabled bool
|
|
isTerminalMode bool
|
|
doGetSnapshot bool
|
|
doneGetSnapshot bool
|
|
}
|
|
|
|
func newWorkspace() *Workspace {
|
|
editor.putLog("initialize workspace")
|
|
ws := &Workspace{
|
|
stop: make(chan struct{}),
|
|
flushCh: make(chan []interface{}, 100),
|
|
foreground: newRGBA(255, 255, 255, 1),
|
|
background: newRGBA(0, 0, 0, 1),
|
|
special: newRGBA(255, 255, 255, 1),
|
|
shouldUpdate: &ShouldUpdate{},
|
|
}
|
|
|
|
return ws
|
|
}
|
|
|
|
func (ws *Workspace) initUI() {
|
|
ws.widget = widgets.NewQWidget(nil, 0)
|
|
ws.widget.SetParent(editor.widget)
|
|
ws.widget.SetAcceptDrops(true)
|
|
ws.widget.ConnectDragEnterEvent(ws.dragEnterEvent)
|
|
ws.widget.ConnectDragMoveEvent(ws.dragMoveEvent)
|
|
ws.widget.ConnectDropEvent(ws.dropEvent)
|
|
|
|
// Basic Workspace UI component
|
|
// screen
|
|
ws.screen = newScreen()
|
|
ws.screen.ws = ws
|
|
ws.screen.initInputMethodWidget()
|
|
|
|
// cursor
|
|
ws.cursor = initCursorNew()
|
|
ws.cursor.SetParent(ws.widget)
|
|
ws.cursor.ws = ws
|
|
// ws.cursor.setBypassScreenEvent()
|
|
|
|
// If ExtFooBar is true, then we create a UI component
|
|
// tabline
|
|
if editor.config.Editor.ExtTabline {
|
|
ws.tabline = initTabline()
|
|
ws.tabline.ws = ws
|
|
ws.tabline.widget.ConnectShowEvent(ws.tabline.showEvent)
|
|
}
|
|
|
|
// cmdline
|
|
if editor.config.Editor.ExtCmdline {
|
|
ws.cmdline = initCmdline()
|
|
ws.cmdline.ws = ws
|
|
}
|
|
|
|
// palette for cmdline
|
|
if editor.config.Editor.ExtCmdline {
|
|
ws.palette = initPalette()
|
|
ws.palette.ws = ws
|
|
ws.palette.widget.SetParent(ws.widget)
|
|
ws.palette.setColor()
|
|
ws.palette.hide()
|
|
}
|
|
|
|
// popupmenu
|
|
if editor.config.Editor.ExtPopupmenu {
|
|
ws.popup = initPopupmenuNew()
|
|
ws.popup.widget.SetParent(ws.widget)
|
|
ws.popup.ws = ws
|
|
ws.popup.widget.Hide()
|
|
}
|
|
|
|
// messages
|
|
if editor.config.Editor.ExtMessages {
|
|
ws.message = initMessage()
|
|
ws.message.ws = ws
|
|
ws.message.widget.SetParent(ws.widget)
|
|
ws.signal.ConnectMessageSignal(func() {
|
|
ws.message.update()
|
|
})
|
|
}
|
|
|
|
if ws.tabline != nil {
|
|
ws.isDrawTabline = editor.config.Tabline.Visible && editor.config.Editor.ExtTabline
|
|
ws.tabline.connectUI()
|
|
}
|
|
if ws.message != nil {
|
|
ws.message.connectUI()
|
|
}
|
|
|
|
// workspace widget, layouts
|
|
layout := widgets.NewQVBoxLayout()
|
|
layout.SetContentsMargins(0, 0, 0, 0)
|
|
layout.SetSpacing(0)
|
|
ws.widget.SetContentsMargins(0, 0, 0, 0)
|
|
ws.widget.SetLayout(layout)
|
|
ws.widget.SetFocusPolicy(core.Qt__StrongFocus)
|
|
ws.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
ws.widget.ConnectInputMethodEvent(ws.InputMethodEvent)
|
|
ws.widget.ConnectInputMethodQuery(ws.InputMethodQuery)
|
|
|
|
// screen widget and scrollBar widget
|
|
widget2 := widgets.NewQWidget(nil, 0)
|
|
widget2.SetContentsMargins(0, 0, 0, 0)
|
|
ws.layout2 = widgets.NewQHBoxLayout()
|
|
ws.layout2.SetContentsMargins(0, 0, 0, 0)
|
|
ws.layout2.SetSpacing(0)
|
|
ws.layout2.AddWidget(ws.screen.widget, 0, 0)
|
|
widget2.SetLayout(ws.layout2)
|
|
|
|
// assemble all neovim ui components
|
|
if editor.config.Editor.ExtTabline {
|
|
layout.AddWidget(ws.tabline.widget, 0, 0)
|
|
}
|
|
layout.AddWidget(widget2, 1, 0)
|
|
|
|
ws.widget.Move2(0, 0)
|
|
|
|
editor.putLog("assembled workspace UI components")
|
|
}
|
|
|
|
func (ws *Workspace) initFont() {
|
|
ws.screen.font = editor.font
|
|
ws.screen.fallbackfonts = editor.fallbackfonts
|
|
ws.font = ws.screen.font
|
|
ws.screen.tooltip.setFont(editor.font)
|
|
ws.screen.tooltip.fallbackfonts = editor.fallbackfonts
|
|
ws.font.ws = ws
|
|
if ws.tabline != nil {
|
|
ws.tabline.font = ws.font.qfont
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) lazyLoadUI() {
|
|
editor.putLog("Start preparing for deferred drawing UI")
|
|
|
|
editor.putLog("preparing scrollbar")
|
|
// scrollbar
|
|
if editor.config.ScrollBar.Visible {
|
|
ws.scrollBar = newScrollBar()
|
|
ws.scrollBar.ws = ws
|
|
}
|
|
|
|
editor.putLog("preparing minimap")
|
|
// minimap
|
|
if !editor.config.MiniMap.Disable {
|
|
ws.minimap = newMiniMap()
|
|
ws.minimap.ws = ws
|
|
ws.layout2.AddWidget(ws.minimap.widget, 0, 0)
|
|
}
|
|
|
|
editor.putLog("preparing deferred drawing UI")
|
|
if editor.config.ScrollBar.Visible {
|
|
ws.layout2.AddWidget(ws.scrollBar.widget, 0, 0)
|
|
ws.scrollBar.setColor()
|
|
}
|
|
|
|
editor.putLog("preparing filer")
|
|
// Add editor feature
|
|
go filer.RegisterPlugin(ws.nvim, editor.config.Editor.FileOpenCmd)
|
|
|
|
editor.putLog("preparing minimap buffer")
|
|
// Asynchronously execute the process for minimap
|
|
if !editor.config.MiniMap.Disable {
|
|
ws.minimap.startMinimapProc(editor.ctx)
|
|
time.Sleep(time.Millisecond * 50)
|
|
ws.minimap.mu.Lock()
|
|
isMinimapVisible := ws.minimap.visible
|
|
ws.minimap.mu.Unlock()
|
|
if isMinimapVisible {
|
|
ws.minimap.bufUpdate()
|
|
ws.minimap.bufSync()
|
|
ws.updateSize()
|
|
}
|
|
}
|
|
|
|
editor.putLog("Finished preparing the deferred drawing UI.")
|
|
}
|
|
|
|
func (ws *Workspace) initLazyLoadUI() {
|
|
editor.isWindowNowActivated = false
|
|
|
|
ws.widget.ConnectFocusInEvent(func(event *gui.QFocusEvent) {
|
|
go ws.nvim.SetFocusUI(true)
|
|
})
|
|
ws.widget.ConnectFocusOutEvent(func(event *gui.QFocusEvent) {
|
|
go ws.nvim.SetFocusUI(false)
|
|
})
|
|
|
|
go func() {
|
|
if !editor.doRestoreSessions {
|
|
time.Sleep(time.Millisecond * 500)
|
|
}
|
|
ws.signal.LazyLoadSignal()
|
|
|
|
if !editor.doRestoreSessions {
|
|
time.Sleep(time.Millisecond * 500)
|
|
}
|
|
editor.signal.SidebarSignal()
|
|
|
|
// put font debug log
|
|
ws.font.putDebugLog()
|
|
}()
|
|
}
|
|
|
|
func (ws *Workspace) registerSignal(signal *neovimSignal, redrawUpdates chan [][]interface{}, guiUpdates chan []interface{}) {
|
|
ws.signal = signal
|
|
ws.redrawUpdates = redrawUpdates
|
|
ws.guiUpdates = guiUpdates
|
|
|
|
ws.signal.ConnectRedrawSignal(func() {
|
|
updates := <-ws.redrawUpdates
|
|
editor.putLog("Received redraw event from neovim")
|
|
ws.handleRedraw(updates)
|
|
})
|
|
ws.signal.ConnectGuiSignal(func() {
|
|
updates := <-ws.guiUpdates
|
|
editor.putLog("Received GUI event from neovim")
|
|
ws.handleGui(updates)
|
|
})
|
|
ws.signal.ConnectLazyLoadSignal(func() {
|
|
if ws.hasLazyUI {
|
|
return
|
|
}
|
|
if editor.config.Editor.ExtTabline {
|
|
ws.tabline.initTab()
|
|
}
|
|
editor.workspaceUpdate()
|
|
ws.hasLazyUI = true
|
|
ws.lazyLoadUI()
|
|
})
|
|
|
|
ws.signal.ConnectStopSignal(func() {
|
|
workspaces := []*Workspace{}
|
|
index := 0
|
|
maxworkspaceIndex := len(editor.workspaces) - 1
|
|
for i, wse := range editor.workspaces {
|
|
if ws != wse {
|
|
workspaces = append(workspaces, wse)
|
|
} else {
|
|
index = i
|
|
}
|
|
}
|
|
|
|
if len(workspaces) == 0 {
|
|
// TODO
|
|
// If nvim is an instance on a remote server, the connection `cmd` can be
|
|
// `ssh` or `wsl` command. What kind of exit status should be set?
|
|
if ws.uiRemoteAttached || ws.nvim == nil {
|
|
editor.close(0)
|
|
} else {
|
|
editor.close(ws.nvim.ExitCode())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
editor.workspaces = workspaces
|
|
|
|
for i := 0; i < len(editor.side.items); i++ {
|
|
if i >= index && i+1 < len(editor.side.items) {
|
|
editor.side.items[i].copy(editor.side.items[i+1])
|
|
}
|
|
if i+1 == len(editor.side.items) {
|
|
editor.side.items[i].label.SetText("")
|
|
editor.side.items[i].hidden = false
|
|
editor.side.items[i].active = false
|
|
editor.side.items[i].text = ""
|
|
editor.side.items[i].cwdpath = ""
|
|
editor.side.items[i].isContentHide = false
|
|
|
|
content := widgets.NewQListWidget(nil)
|
|
content.SetFocusPolicy(core.Qt__NoFocus)
|
|
content.SetFrameShape(widgets.QFrame__NoFrame)
|
|
content.SetHorizontalScrollBarPolicy(core.Qt__ScrollBarAlwaysOff)
|
|
content.SetFont(editor.font.qfont)
|
|
content.SetIconSize(core.NewQSize2(editor.iconSize*3/4, editor.iconSize*3/4))
|
|
editor.side.items[i].content = content
|
|
editor.side.items[i].widget.Layout().AddWidget(content)
|
|
}
|
|
if i == maxworkspaceIndex {
|
|
editor.side.items[i].hidden = true
|
|
editor.side.items[i].hidden = false
|
|
}
|
|
editor.side.items[i].setSideItemLabel(i)
|
|
}
|
|
|
|
ws.hide()
|
|
if editor.active == index {
|
|
if index > 0 {
|
|
editor.active--
|
|
}
|
|
editor.workspaceUpdate()
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
func (ws *Workspace) bindNvim(nvimCh chan *nvim.Nvim, uiRemoteAttachedCh chan bool, isSetWindowState, isLazyBind bool, file string) {
|
|
ws.nvim = <-nvimCh
|
|
ws.uiRemoteAttached = <-uiRemoteAttachedCh
|
|
|
|
// Get nvim options
|
|
ws.getGlobalOptions()
|
|
|
|
// Adjust nvim geometry to fit application window size
|
|
ws.uiAttached = true
|
|
if len(editor.workspaces) == 1 {
|
|
editor.chUiPrepared <- true
|
|
}
|
|
|
|
// Load goneovim's neovim settings
|
|
loadHelpDoc(ws.nvim)
|
|
loadGinitVim(ws.nvim)
|
|
source(ws.nvim, file)
|
|
|
|
// Initialize lazy load UI
|
|
ws.initLazyLoadUI()
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) copy(ii *WorkspaceSideItem) {
|
|
i.label.SetText(ii.label.Text())
|
|
i.hidden = ii.hidden
|
|
i.active = ii.active
|
|
i.text = ii.text
|
|
i.cwdpath = ii.cwdpath
|
|
i.content = ii.content
|
|
i.isContentHide = ii.isContentHide
|
|
|
|
i.widget.Layout().AddWidget(i.content)
|
|
|
|
}
|
|
|
|
func (ws *Workspace) hide() {
|
|
if ws.hidden {
|
|
return
|
|
}
|
|
ws.hidden = true
|
|
ws.widget.Hide()
|
|
}
|
|
|
|
func (ws *Workspace) show() {
|
|
if !ws.hidden {
|
|
return
|
|
}
|
|
ws.hidden = false
|
|
ws.widget.Show()
|
|
ws.widget.SetFocus2Default()
|
|
ws.cursor.update()
|
|
}
|
|
|
|
func (ws *Workspace) getGlobalOptions() {
|
|
ws.getColorscheme()
|
|
ws.getBG()
|
|
ws.getKeymaps()
|
|
ws.getMousescroll()
|
|
}
|
|
|
|
func (ws *Workspace) getMousescroll() {
|
|
msChan := make(chan string, 5)
|
|
go func() {
|
|
mousescroll := ""
|
|
ws.nvim.Option("mousescroll", &mousescroll)
|
|
msChan <- mousescroll
|
|
}()
|
|
|
|
select {
|
|
case ws.mouseScroll = <-msChan:
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) getColorscheme() {
|
|
strChan := make(chan string, 5)
|
|
go func() {
|
|
colorscheme := ""
|
|
ws.nvim.Var("colors_name", &colorscheme)
|
|
strChan <- colorscheme
|
|
}()
|
|
select {
|
|
case colo := <-strChan:
|
|
ws.colorscheme = colo
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) getBG() {
|
|
strChan := make(chan string, 5)
|
|
go func() {
|
|
screenbg := "dark"
|
|
ws.nvim.Option("background", &screenbg)
|
|
strChan <- screenbg
|
|
}()
|
|
|
|
select {
|
|
case screenbg := <-strChan:
|
|
ws.screenbg = screenbg
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) getKeymaps() {
|
|
ws.escKeyInInsert = "<Esc>"
|
|
ws.escKeyInNormal = "<Esc>"
|
|
|
|
nmapChan := make(chan []*nvim.Mapping, 5)
|
|
imapChan := make(chan []*nvim.Mapping, 5)
|
|
|
|
// Get user mappings
|
|
go func() {
|
|
var nmappings, imappings []*nvim.Mapping
|
|
var err1, err2 error
|
|
nmappings, err1 = ws.nvim.KeyMap("normal")
|
|
if err1 != nil {
|
|
return
|
|
}
|
|
nmapChan <- nmappings
|
|
imappings, err2 = ws.nvim.KeyMap("insert")
|
|
if err2 != nil {
|
|
return
|
|
}
|
|
imapChan <- imappings
|
|
}()
|
|
|
|
// wait to getting user mappings
|
|
var ok [2]bool
|
|
for {
|
|
select {
|
|
case nmappings := <-nmapChan:
|
|
ws.normalMappings = nmappings
|
|
ok[0] = true
|
|
case imappings := <-imapChan:
|
|
ws.insertMappings = imappings
|
|
ok[1] = true
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
ok[0] = true
|
|
ok[1] = true
|
|
}
|
|
|
|
if ok[0] && ok[1] {
|
|
break
|
|
}
|
|
}
|
|
|
|
altkeyCount := 0
|
|
metakeyCount := 0
|
|
for _, mapping := range ws.insertMappings {
|
|
// Check Esc mapping
|
|
if strings.EqualFold(mapping.RHS, "<Esc>") || strings.EqualFold(mapping.RHS, "<C-[>") {
|
|
if mapping.NoRemap == 1 {
|
|
ws.escKeyInInsert = mapping.LHS
|
|
}
|
|
}
|
|
// Count user def alt/meta key mappings
|
|
if strings.HasPrefix(mapping.LHS, "<A-") {
|
|
altkeyCount++
|
|
}
|
|
if strings.HasPrefix(mapping.LHS, "<M-") {
|
|
metakeyCount++
|
|
}
|
|
}
|
|
for _, mapping := range ws.normalMappings {
|
|
if strings.EqualFold(mapping.RHS, "<Esc>") || strings.EqualFold(mapping.RHS, "<C-[>") {
|
|
if mapping.NoRemap == 1 {
|
|
ws.escKeyInNormal = mapping.LHS
|
|
}
|
|
}
|
|
if strings.EqualFold(mapping.LHS, "<C-y>") || strings.EqualFold(mapping.LHS, "<C-e>") {
|
|
ws.isMappingScrollKey = true
|
|
}
|
|
// Count user def alt/meta key mappings
|
|
if strings.HasPrefix(mapping.LHS, "<A-") {
|
|
altkeyCount++
|
|
}
|
|
if strings.HasPrefix(mapping.LHS, "<M-") {
|
|
metakeyCount++
|
|
}
|
|
}
|
|
|
|
editor.muMetaKey.Lock()
|
|
if altkeyCount >= metakeyCount {
|
|
editor.prefixToMapMetaKey = "A-"
|
|
} else {
|
|
editor.prefixToMapMetaKey = "M-"
|
|
}
|
|
editor.muMetaKey.Unlock()
|
|
}
|
|
|
|
func (ws *Workspace) getNumOfTabs() int {
|
|
done := make(chan int, 5)
|
|
num := 0
|
|
go func() {
|
|
tn := 0
|
|
ws.nvim.Eval("tabpagenr('$')", &tn)
|
|
done <- tn
|
|
}()
|
|
select {
|
|
case tn := <-done:
|
|
num = tn
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
}
|
|
|
|
return num
|
|
}
|
|
|
|
func (ws *Workspace) getCwd() string {
|
|
done := make(chan bool, 5)
|
|
cwd := ""
|
|
go func() {
|
|
ws.nvim.Eval("getcwd()", &cwd)
|
|
done <- true
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
}
|
|
|
|
return cwd
|
|
}
|
|
|
|
func (ws *Workspace) nvimEval(s string) (interface{}, error) {
|
|
doneChannel := make(chan interface{}, 5)
|
|
var result interface{}
|
|
go func() {
|
|
ws.nvim.Eval(s, &result)
|
|
doneChannel <- result
|
|
}()
|
|
select {
|
|
case done := <-doneChannel:
|
|
return done, nil
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
err := errors.New("neovim busy")
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) handleChangeCwd(cwdinfo map[string]interface{}) {
|
|
scope, ok := cwdinfo["scope"]
|
|
if !ok {
|
|
scope = "global"
|
|
}
|
|
cwdITF, ok := cwdinfo["cwd"]
|
|
if !ok {
|
|
return
|
|
}
|
|
cwd := cwdITF.(string)
|
|
switch scope {
|
|
case "global":
|
|
ws.setCwd(cwd)
|
|
case "tab":
|
|
ws.setCwdInTab(cwd)
|
|
case "window":
|
|
ws.setCwdInWin(cwd)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) setCwd(cwd string) {
|
|
if cwd == "" {
|
|
cwd = ws.getCwd()
|
|
}
|
|
ws.cwd = cwd
|
|
|
|
var labelpath string
|
|
switch editor.config.Workspace.PathStyle {
|
|
case "name":
|
|
labelpath = filepath.Base(cwd)
|
|
case "minimum":
|
|
labelpath, _ = shortpath.Minimum(cwd)
|
|
case "full":
|
|
labelpath, _ = filepath.Abs(cwd)
|
|
default:
|
|
labelpath, _ = filepath.Abs(cwd)
|
|
}
|
|
ws.cwdlabel = labelpath
|
|
ws.cwdBase = filepath.Base(cwd)
|
|
if editor.side == nil {
|
|
return
|
|
}
|
|
for i, wse := range editor.workspaces {
|
|
if i >= len(editor.side.items) {
|
|
return
|
|
}
|
|
|
|
if ws == wse {
|
|
path, _ := filepath.Abs(cwd)
|
|
sideItem := editor.side.items[i]
|
|
if sideItem.cwdpath == path {
|
|
continue
|
|
}
|
|
|
|
sideItem.label.SetText(wse.cwdlabel)
|
|
sideItem.label.SetFont(editor.font.qfont)
|
|
sideItem.cwdpath = path
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) setCwdInTab(cwd string) {
|
|
ws.screen.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
return true
|
|
}
|
|
if win.isShown() {
|
|
win.cwd = cwd
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (ws *Workspace) setCwdInWin(cwd string) {
|
|
ws.screen.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
return true
|
|
}
|
|
if win.grid == ws.cursor.gridid {
|
|
win.cwd = cwd
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (ws *Workspace) updateSize() (windowWidth, windowHeight, cols, rows int) {
|
|
e := editor
|
|
|
|
geometry := e.window.Geometry()
|
|
width := geometry.Width()
|
|
|
|
marginWidth := e.window.BorderSize()*4 + e.window.WindowGap()*2
|
|
sideWidth := 0
|
|
if e.side != nil {
|
|
if e.side.widget.IsVisible() {
|
|
sideWidth = e.splitter.Sizes()[0] + e.splitter.HandleWidth()
|
|
}
|
|
}
|
|
width -= marginWidth + sideWidth
|
|
|
|
height := geometry.Height()
|
|
|
|
marginHeight := e.window.BorderSize() * 4
|
|
height -= marginHeight
|
|
|
|
titlebarHeight := 0
|
|
if e.config.Editor.BorderlessWindow && runtime.GOOS != "linux" {
|
|
if !e.config.Editor.HideTitlebar {
|
|
titlebarHeight = e.window.TitleBar.Height()
|
|
}
|
|
}
|
|
height -= titlebarHeight
|
|
|
|
tablineHeight := 0
|
|
if ws.isDrawTabline && ws.tabline != nil {
|
|
if ws.tabline.showtabline != -1 {
|
|
ws.tabline.height = ws.tabline.Tabs[0].widget.Height() + (TABLINEMARGIN * 2)
|
|
tablineHeight = ws.tabline.height
|
|
}
|
|
}
|
|
|
|
scrollbarWidth := 0
|
|
if e.config.ScrollBar.Visible {
|
|
scrollbarWidth = e.config.ScrollBar.Width
|
|
}
|
|
|
|
minimapWidth := 0
|
|
if ws.minimap != nil {
|
|
if ws.minimap.visible {
|
|
minimapWidth = e.config.MiniMap.Width
|
|
}
|
|
}
|
|
|
|
screenWidth := width - scrollbarWidth - minimapWidth
|
|
screenHeight := height - tablineHeight
|
|
|
|
if ws.screen.font == nil {
|
|
fmt.Println("nil!")
|
|
}
|
|
rw := screenWidth - int(math.Ceil(float64(int(float64(screenWidth)/ws.screen.font.cellwidth))*ws.screen.font.cellwidth))
|
|
rh := screenHeight % ws.screen.font.lineHeight
|
|
screenWidth -= rw
|
|
screenHeight -= rh
|
|
width -= rw
|
|
height -= rh
|
|
|
|
if width != ws.width || height != ws.height {
|
|
ws.width = width
|
|
ws.height = height
|
|
ws.widget.Resize2(width, height)
|
|
if !ws.hidden {
|
|
ws.hide()
|
|
ws.show()
|
|
} else {
|
|
ws.show()
|
|
ws.hide()
|
|
}
|
|
}
|
|
|
|
if ws.screen != nil {
|
|
ws.screen.width = screenWidth
|
|
ws.screen.height = screenHeight
|
|
ws.screen.updateSize()
|
|
}
|
|
if ws.cursor != nil {
|
|
ws.cursor.resize(ws.cursor.width, ws.cursor.height)
|
|
ws.cursor.update()
|
|
}
|
|
if ws.palette != nil {
|
|
ws.palette.resize()
|
|
}
|
|
if ws.message != nil {
|
|
ws.message.resize()
|
|
}
|
|
|
|
windowWidth = marginWidth + sideWidth + scrollbarWidth + minimapWidth + ws.screen.width
|
|
windowHeight = marginHeight + titlebarHeight + tablineHeight + ws.screen.height
|
|
cols = ws.cols
|
|
rows = ws.rows
|
|
|
|
return
|
|
}
|
|
|
|
func (ws *Workspace) updateApplicationWindowSize(cols, rows int) {
|
|
e := editor
|
|
font := ws.font
|
|
|
|
if e.window.WindowState() == core.Qt__WindowFullScreen ||
|
|
e.window.WindowState() == core.Qt__WindowMaximized {
|
|
return
|
|
}
|
|
|
|
appWinWidth := int(math.Ceil(font.cellwidth * float64(cols)))
|
|
appWinHeight := int(float64(font.lineHeight) * float64(rows))
|
|
|
|
marginWidth := e.window.BorderSize()*4 + e.window.WindowGap()*2
|
|
sideWidth := 0
|
|
if e.side != nil {
|
|
if e.side.widget.IsVisible() {
|
|
sideWidth = e.splitter.Sizes()[0] + e.splitter.HandleWidth()
|
|
}
|
|
}
|
|
appWinWidth += marginWidth + sideWidth
|
|
|
|
marginHeight := e.window.BorderSize() * 4
|
|
titlebarHeight := 0
|
|
if e.config.Editor.BorderlessWindow && runtime.GOOS != "linux" {
|
|
if !e.config.Editor.HideTitlebar {
|
|
titlebarHeight = e.window.TitleBar.Height()
|
|
}
|
|
}
|
|
appWinHeight += marginHeight + titlebarHeight
|
|
|
|
tablineHeight := 0
|
|
if ws.isDrawTabline && ws.tabline != nil {
|
|
if ws.tabline.showtabline != -1 {
|
|
ws.tabline.height = ws.tabline.Tabs[0].widget.Height() + (TABLINEMARGIN * 2)
|
|
tablineHeight = ws.tabline.height
|
|
}
|
|
}
|
|
|
|
scrollbarWidth := 0
|
|
if e.config.ScrollBar.Visible {
|
|
scrollbarWidth = e.config.ScrollBar.Width
|
|
}
|
|
|
|
minimapWidth := 0
|
|
if ws.minimap != nil {
|
|
if ws.minimap.visible {
|
|
minimapWidth = e.config.MiniMap.Width
|
|
}
|
|
}
|
|
|
|
appWinWidth += scrollbarWidth + minimapWidth
|
|
appWinHeight += tablineHeight
|
|
|
|
// Disable size specifications larger than the desktop screen size
|
|
desktopRect := e.app.Desktop().AvailableGeometry2(e.window)
|
|
desktopWidth := desktopRect.Width()
|
|
desktopHeight := desktopRect.Height()
|
|
if appWinWidth > desktopWidth {
|
|
appWinWidth = desktopWidth
|
|
}
|
|
if appWinHeight > desktopHeight {
|
|
appWinHeight = desktopHeight
|
|
}
|
|
|
|
e.putLog("update app win size::", appWinWidth, appWinHeight)
|
|
|
|
e.window.Resize2(
|
|
appWinWidth,
|
|
appWinHeight,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
func (e *Editor) updateNotificationPos() {
|
|
e.width = e.window.Width()
|
|
e.height = e.window.Height()
|
|
e.notifyStartPos = core.NewQPoint2(e.width-e.notificationWidth-10, e.height-30)
|
|
var x, y int
|
|
var newNotifications []*Notification
|
|
for _, item := range e.notifications {
|
|
x = e.notifyStartPos.X()
|
|
y = e.notifyStartPos.Y() - item.widget.Height() - 4
|
|
if !item.isHide && !item.isMoved {
|
|
item.widget.Move2(x, y)
|
|
e.notifyStartPos = core.NewQPoint2(x, y)
|
|
}
|
|
newNotifications = append(newNotifications, item)
|
|
}
|
|
e.notifications = newNotifications
|
|
}
|
|
|
|
func handleEvent(update interface{}) (event string, ok bool) {
|
|
switch update.(type) {
|
|
case string:
|
|
event = update.(string)
|
|
ok = true
|
|
default:
|
|
event = ""
|
|
ok = false
|
|
}
|
|
|
|
return event, ok
|
|
}
|
|
|
|
func (ws *Workspace) handleRedraw(updates [][]interface{}) {
|
|
|
|
ws.doGetSnapshot = ws.shouldGetSnapshot(updates)
|
|
if ws.doGetSnapshot {
|
|
ws.getSnapshot()
|
|
ws.doneGetSnapshot = true
|
|
}
|
|
|
|
for _, update := range updates {
|
|
event, ok := handleEvent(update[0])
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
args := update[1:]
|
|
editor.putLog("start ", event)
|
|
|
|
switch event {
|
|
case "set_title":
|
|
ws.setTitle(args)
|
|
case "set_icon":
|
|
case "mode_info_set":
|
|
ws.modeInfoSet(args)
|
|
case "option_set":
|
|
ws.optionSet(args)
|
|
case "mode_change":
|
|
ws.modeChange(args)
|
|
|
|
case "mouse_on":
|
|
ws.mouseOn()
|
|
case "mouse_off":
|
|
ws.mouseOff()
|
|
|
|
case "busy_start":
|
|
ws.busyStart()
|
|
case "busy_stop":
|
|
ws.busyStop()
|
|
|
|
case "suspend":
|
|
case "update_menu":
|
|
case "bell":
|
|
case "visual_bell":
|
|
|
|
case "flush":
|
|
ws.flush()
|
|
|
|
// Grid Events
|
|
case "grid_resize":
|
|
ws.gridResize(args)
|
|
case "default_colors_set":
|
|
ws.defaultColorsSet(args)
|
|
case "hl_attr_define":
|
|
ws.hlAttrDefine(args)
|
|
case "hl_group_set":
|
|
ws.hlGroupSet(args)
|
|
|
|
case "grid_line":
|
|
ws.flushCh <- update
|
|
case "grid_clear":
|
|
ws.flushCh <- update
|
|
case "grid_destroy":
|
|
ws.flushCh <- update
|
|
case "grid_cursor_goto":
|
|
ws.flushCh <- update
|
|
case "grid_scroll":
|
|
ws.flushCh <- update
|
|
|
|
// Multigrid Events
|
|
case "win_pos":
|
|
ws.winPos(args)
|
|
case "win_float_pos":
|
|
ws.winFloatPos(args)
|
|
case "win_external_pos":
|
|
ws.winExternalPos(args)
|
|
case "win_hide":
|
|
ws.winHide(args)
|
|
case "win_close":
|
|
ws.winClose()
|
|
case "msg_set_pos":
|
|
ws.msgSetPos(args)
|
|
case "win_viewport":
|
|
ws.flushCh <- update
|
|
case "win_viewport_margins":
|
|
ws.flushCh <- update
|
|
|
|
// Popupmenu Events
|
|
case "popupmenu_show":
|
|
ws.popupmenuShow(args)
|
|
case "popupmenu_select":
|
|
ws.popupmenuSelect(args)
|
|
case "popupmenu_hide":
|
|
ws.popupmenuHide(args)
|
|
|
|
// Tabline Events
|
|
case "tabline_update":
|
|
ws.tablineUpdate(args)
|
|
|
|
// Cmdline Events
|
|
case "cmdline_show":
|
|
ws.cmdlineShow(args)
|
|
case "cmdline_pos":
|
|
ws.cmdlinePos(args)
|
|
case "cmdline_special_char":
|
|
case "cmdline_char":
|
|
ws.cmdlineChar(args)
|
|
case "cmdline_hide":
|
|
ws.cmdlineHide(args)
|
|
case "cmdline_function_show":
|
|
ws.cmdlineFunctionShow(args)
|
|
case "cmdline_function_hide":
|
|
ws.cmdlineFunctionHide(args)
|
|
case "cmdline_block_show":
|
|
case "cmdline_block_append":
|
|
case "cmdline_block_hide":
|
|
|
|
// Message/Dialog Events
|
|
case "msg_show":
|
|
ws.msgShow(args)
|
|
case "msg_clear":
|
|
ws.msgClear()
|
|
case "msg_showmode":
|
|
case "msg_showcmd":
|
|
case "msg_ruler":
|
|
case "msg_history_show":
|
|
ws.msgHistoryShow(args)
|
|
default:
|
|
}
|
|
|
|
editor.putLog("finished", event)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) flush() {
|
|
close(ws.flushCh)
|
|
for update := range ws.flushCh {
|
|
event, ok := handleEvent(update[0])
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
args := update[1:]
|
|
switch event {
|
|
case "grid_line":
|
|
ws.gridLine(args)
|
|
case "grid_clear":
|
|
ws.gridClear(args)
|
|
case "grid_destroy":
|
|
ws.gridDestroy(args)
|
|
case "grid_cursor_goto":
|
|
ws.gridCursorGoto(args)
|
|
case "grid_scroll":
|
|
ws.gridScroll(args)
|
|
case "win_viewport":
|
|
ws.winViewport(args)
|
|
case "win_viewport_margins":
|
|
ws.winViewportMargins(args)
|
|
}
|
|
}
|
|
ws.flushCh = make(chan []interface{}, 100)
|
|
ws.doneGetSnapshot = false
|
|
|
|
if ws.shouldUpdate.globalgrid {
|
|
ws.screen.detectCoveredCellInGlobalgrid()
|
|
ws.shouldUpdate.globalgrid = false
|
|
}
|
|
|
|
// update cursor
|
|
if ws.shouldUpdate.cursor {
|
|
ws.cursor.update()
|
|
ws.shouldUpdate.cursor = false
|
|
}
|
|
|
|
// update screen
|
|
ws.screen.update()
|
|
|
|
// update external scrollbar
|
|
ws.updateScrollbar()
|
|
|
|
// update IME tooltip
|
|
ws.updateIMETooltip()
|
|
|
|
// update minimap
|
|
if ws.shouldUpdate.minimap {
|
|
ws.updateMinimap()
|
|
ws.shouldUpdate.minimap = false
|
|
}
|
|
|
|
ws.maxLineDelta = 0
|
|
}
|
|
|
|
func (ws *Workspace) updateScrollbar() {
|
|
if ws.scrollBar != nil {
|
|
if editor.config.ScrollBar.Visible {
|
|
ws.scrollBar.update()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) updateIMETooltip() {
|
|
if ws.screen.tooltip.IsVisible() {
|
|
x, y, _, _ := ws.screen.tooltip.pos()
|
|
ws.screen.tooltip.move(x, y)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) updateMinimap() {
|
|
if ws.minimap != nil {
|
|
if ws.minimap.visible && ws.minimap.widget.IsVisible() {
|
|
ws.scrollMinimap()
|
|
ws.minimap.mapScroll()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) disableImeInNormal() {
|
|
if !editor.config.Editor.DisableImeInNormal {
|
|
return
|
|
}
|
|
switch ws.mode {
|
|
case "insert":
|
|
ws.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
editor.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
case "cmdline_normal":
|
|
ws.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
editor.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
default:
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) shouldGetSnapshot(updates [][]interface{}) bool {
|
|
doGetSnapshot := false
|
|
for _, update := range updates {
|
|
event, ok := handleEvent(update[0])
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
args := update[1:]
|
|
|
|
switch event {
|
|
case "grid_scroll":
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
if gridid == 1 {
|
|
continue
|
|
}
|
|
|
|
win, ok := ws.screen.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if win.isMsgGrid {
|
|
continue
|
|
}
|
|
|
|
cols := arg.([]interface{})[5]
|
|
if cols != 0 {
|
|
doGetSnapshot = true
|
|
}
|
|
|
|
}
|
|
case "win_viewport":
|
|
delta := -1
|
|
for _, a := range args {
|
|
arg := a.([]interface{})
|
|
if len(arg) >= 8 {
|
|
delta = util.ReflectToInt(arg[7])
|
|
}
|
|
grid := util.ReflectToInt(arg[0])
|
|
win, ok := ws.screen.getWindow(grid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if win.grid == 1 || win.isMsgGrid {
|
|
continue
|
|
}
|
|
|
|
if delta != 0 {
|
|
doGetSnapshot = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ws.doneGetSnapshot {
|
|
doGetSnapshot = false
|
|
}
|
|
|
|
return doGetSnapshot
|
|
}
|
|
|
|
func (ws *Workspace) modeEnablingIME(mode string) {
|
|
if len(editor.config.Editor.ModeEnablingIME) == 0 {
|
|
return
|
|
}
|
|
// if ws.mode == mode {
|
|
// return
|
|
// }
|
|
if ws.isTerminalMode {
|
|
mode = "terminal"
|
|
}
|
|
doEnable := false
|
|
|
|
for _, m := range editor.config.Editor.ModeEnablingIME {
|
|
if mode == m {
|
|
doEnable = true
|
|
}
|
|
}
|
|
if doEnable {
|
|
ws.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
editor.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
} else {
|
|
ws.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, false)
|
|
editor.widget.SetAttribute(core.Qt__WA_InputMethodEnabled, false)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) setDefaultColorsSet(args []interface{}) {
|
|
fg := util.ReflectToInt(args[0])
|
|
bg := util.ReflectToInt(args[1])
|
|
sp := util.ReflectToInt(args[2])
|
|
|
|
editor.putLog("default colors set", fg, bg, sp)
|
|
|
|
if fg != -1 {
|
|
ws.foreground.R = calcColor(fg).R
|
|
ws.foreground.G = calcColor(fg).G
|
|
ws.foreground.B = calcColor(fg).B
|
|
}
|
|
if bg != -1 {
|
|
ws.background.R = calcColor(bg).R
|
|
ws.background.G = calcColor(bg).G
|
|
ws.background.B = calcColor(bg).B
|
|
}
|
|
if sp != -1 {
|
|
ws.special.R = calcColor(sp).R
|
|
ws.special.G = calcColor(sp).G
|
|
ws.special.B = calcColor(sp).B
|
|
}
|
|
|
|
editor.putLog(bg, ws.background.R, ws.background.G, ws.background.B)
|
|
|
|
var isChangeFg, isChangeBg bool
|
|
if editor.colors.fg != nil {
|
|
isChangeFg = !editor.colors.fg.equals(ws.foreground)
|
|
}
|
|
if editor.colors.bg != nil {
|
|
isChangeBg = !editor.colors.bg.equals(ws.background)
|
|
}
|
|
|
|
if isChangeFg || isChangeBg {
|
|
editor.isSetGuiColor = false
|
|
editor.putLog("isSetGuiColor:", editor.isSetGuiColor)
|
|
}
|
|
|
|
// If it is the second or subsequent nvim instance
|
|
if len(editor.workspaces) > 1 {
|
|
ws.updateWorkspaceColor()
|
|
// Ignore setting GUI color when create second workspace and fg, bg equals -1
|
|
if fg == -1 && bg == -1 {
|
|
editor.isSetGuiColor = true
|
|
}
|
|
}
|
|
|
|
// Exit if there is no change in foreground / background
|
|
if editor.isSetGuiColor {
|
|
return
|
|
}
|
|
|
|
editor.colors.fg = ws.foreground.copy()
|
|
editor.colors.bg = ws.background.copy()
|
|
// Reset hlAttrDef map 0 index:
|
|
if ws.screen.hlAttrDef != nil {
|
|
ws.screen.hlAttrDef[0] = &Highlight{
|
|
foreground: editor.colors.fg,
|
|
background: editor.colors.bg,
|
|
}
|
|
}
|
|
|
|
editor.colors.update()
|
|
if !(ws.colorscheme == "" && fg == -1 && bg == -1 && ws.screenbg == "dark") {
|
|
editor.putLog(ws.colorscheme, fg, bg, ws.screenbg)
|
|
editor.updateGUIColor()
|
|
}
|
|
editor.isSetGuiColor = true
|
|
}
|
|
|
|
func (ws *Workspace) updateWorkspaceColor() {
|
|
editor.putLog("update Workspace Color")
|
|
|
|
if ws.popup != nil {
|
|
ws.popup.setColor()
|
|
}
|
|
|
|
if ws.message != nil {
|
|
ws.message.setColor()
|
|
}
|
|
|
|
// ws.screen.setColor()
|
|
|
|
if ws.cursor != nil {
|
|
ws.cursor.setColor()
|
|
}
|
|
|
|
if ws.scrollBar != nil {
|
|
if editor.config.ScrollBar.Visible {
|
|
ws.scrollBar.setColor()
|
|
}
|
|
}
|
|
|
|
if editor.side != nil {
|
|
editor.side.setColor()
|
|
editor.side.setColorForItems()
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) setTitle(args []interface{}) {
|
|
titleStr := (args[0].([]interface{}))[0].(string)
|
|
editor.window.SetupTitle(titleStr)
|
|
if runtime.GOOS == "linux" {
|
|
editor.window.SetWindowTitle(titleStr)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) modeInfoSet(args []interface{}) {
|
|
for _, arg := range args {
|
|
ws.cursorStyleEnabled = arg.([]interface{})[0].(bool)
|
|
modePropList := arg.([]interface{})[1].([]interface{})
|
|
ws.modeInfo = make([]map[string]interface{}, len(modePropList))
|
|
ws.cursor.isNeedUpdateModeInfo = true
|
|
for i, modeProp := range modePropList {
|
|
// Note: i is the index which given by the `mode_idx` of the `mode_change` event
|
|
ws.modeInfo[i] = modeProp.(map[string]interface{})
|
|
}
|
|
}
|
|
|
|
ws.cursor.modeIdx = 0
|
|
}
|
|
|
|
func (ws *Workspace) optionSet(args []interface{}) {
|
|
for _, option := range args {
|
|
key := (option.([]interface{}))[0].(string)
|
|
val := (option.([]interface{}))[1]
|
|
switch key {
|
|
case "arabicshape":
|
|
case "ambiwidth":
|
|
case "emoji":
|
|
case "guifont":
|
|
ws.guiFont(val.(string))
|
|
case "guifontset":
|
|
case "guifontwide":
|
|
ws.guiFontWide(val.(string))
|
|
case "linespace":
|
|
ws.guiLinespace(val)
|
|
case "pumblend":
|
|
ws.setPumblend(val)
|
|
if ws.popup != nil {
|
|
ws.popup.setPumblend(ws.pb)
|
|
}
|
|
case "showtabline":
|
|
ws.showtabline = util.ReflectToInt(val)
|
|
case "termguicolors":
|
|
// case "ext_cmdline":
|
|
// case "ext_hlstate":
|
|
// case "ext_linegrid":
|
|
// case "ext_multigrid":
|
|
// case "ext_messages":
|
|
// case "ext_popupmenu":
|
|
// case "ext_tabline":
|
|
// case "ext_termcolors":
|
|
default:
|
|
}
|
|
}
|
|
|
|
// Set Transparent blue effect
|
|
if runtime.GOOS == "darwin" && editor.config.Editor.EnableBackgroundBlur {
|
|
isLight := ws.screenbg == "light"
|
|
editor.window.SetBlurEffectForMacOS(isLight)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) msgHistoryShow(args []interface{}) {
|
|
if ws.message != nil {
|
|
ws.message.msgHistoryShow(args)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) msgClear() {
|
|
if ws.message != nil {
|
|
ws.message.msgClear()
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) msgShow(args []interface{}) {
|
|
if ws.message != nil {
|
|
ws.message.msgShow(args)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) cmdlineFunctionHide(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
ws.cmdline.functionHide()
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) cmdlineFunctionShow(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
ws.cmdline.functionShow()
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) cmdlineHide(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
ws.cmdline.hide()
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) cmdlineChar(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
ws.cmdline.putChar(args)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) cmdlineSpecialChar(args []interface{}) {
|
|
}
|
|
|
|
func (ws *Workspace) cmdlinePos(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
ws.cmdline.changePos(args)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) cmdlineShow(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
ws.cmdline.show(args)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) tablineUpdate(args []interface{}) {
|
|
if ws.tabline != nil {
|
|
ws.tabline.handle(args)
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) popupmenuHide(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
if ws.cmdline.shown {
|
|
ws.cmdline.cmdWildmenuHide()
|
|
}
|
|
}
|
|
if ws.popup != nil {
|
|
if ws.cmdline != nil {
|
|
if !ws.cmdline.shown {
|
|
ws.popup.hide()
|
|
}
|
|
} else {
|
|
ws.popup.hide()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) popupmenuSelect(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
if ws.cmdline.shown {
|
|
ws.cmdline.cmdWildmenuSelect(args)
|
|
}
|
|
}
|
|
if ws.popup != nil {
|
|
if ws.cmdline != nil {
|
|
if !ws.cmdline.shown {
|
|
ws.popup.selectItem(args)
|
|
}
|
|
} else {
|
|
ws.popup.selectItem(args)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) popupmenuShow(args []interface{}) {
|
|
if ws.cmdline != nil {
|
|
if ws.cmdline.shown {
|
|
ws.cmdline.cmdWildmenuShow(args)
|
|
}
|
|
}
|
|
if ws.popup != nil {
|
|
if ws.cmdline != nil {
|
|
if !ws.cmdline.shown {
|
|
ws.popup.showItems(args)
|
|
}
|
|
} else {
|
|
ws.popup.showItems(args)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) msgSetPos(args []interface{}) {
|
|
ws.screen.msgSetPos(args)
|
|
}
|
|
|
|
func (ws *Workspace) winClose() {
|
|
ws.screen.windowClose()
|
|
}
|
|
|
|
func (ws *Workspace) winHide(args []interface{}) {
|
|
ws.screen.windowHide(args)
|
|
}
|
|
|
|
func (ws *Workspace) winExternalPos(args []interface{}) {
|
|
ws.screen.windowExternalPosition(args)
|
|
}
|
|
|
|
func (ws *Workspace) winFloatPos(args []interface{}) {
|
|
ws.screen.windowFloatPosition(args)
|
|
}
|
|
|
|
func (ws *Workspace) winPos(args []interface{}) {
|
|
ws.screen.windowPosition(args)
|
|
ws.shouldUpdate.globalgrid = true
|
|
}
|
|
|
|
func (ws *Workspace) gridScroll(args []interface{}) {
|
|
ws.screen.gridScroll(args)
|
|
ws.shouldUpdate.minimap = true
|
|
}
|
|
|
|
func (ws *Workspace) gridCursorGoto(args []interface{}) {
|
|
ws.screen.gridCursorGoto(args)
|
|
ws.shouldUpdate.cursor = true
|
|
ws.shouldUpdate.minimap = true
|
|
}
|
|
|
|
func (ws *Workspace) gridDestroy(args []interface{}) {
|
|
ws.screen.gridDestroy(args)
|
|
}
|
|
|
|
func (ws *Workspace) gridClear(args []interface{}) {
|
|
ws.screen.gridClear(args)
|
|
}
|
|
|
|
func (ws *Workspace) gridLine(args []interface{}) {
|
|
ws.screen.gridLine(args)
|
|
ws.shouldUpdate.cursor = true
|
|
ws.shouldUpdate.minimap = true
|
|
}
|
|
|
|
func (ws *Workspace) hlGroupSet(args []interface{}) {
|
|
ws.screen.setHighlightGroup(args)
|
|
}
|
|
|
|
func (ws *Workspace) hlAttrDefine(args []interface{}) {
|
|
ws.screen.setHlAttrDef(args)
|
|
}
|
|
|
|
func (ws *Workspace) defaultColorsSet(args []interface{}) {
|
|
for _, u := range args {
|
|
ws.setDefaultColorsSet(u.([]interface{}))
|
|
}
|
|
|
|
// Purge all text cache for window's
|
|
ws.screen.purgeTextCacheForWins()
|
|
|
|
}
|
|
|
|
func (ws *Workspace) gridResize(args []interface{}) {
|
|
ws.screen.gridResize(args)
|
|
ws.shouldUpdate.globalgrid = true
|
|
}
|
|
|
|
func (ws *Workspace) busyStart() {
|
|
ws.cursor.isBusy = true
|
|
ws.shouldUpdate.cursor = true
|
|
}
|
|
|
|
func (ws *Workspace) busyStop() {
|
|
ws.cursor.isBusy = false
|
|
ws.shouldUpdate.cursor = true
|
|
}
|
|
|
|
func (ws *Workspace) mouseOn() {
|
|
ws.isMouseEnabled = true
|
|
}
|
|
|
|
func (ws *Workspace) mouseOff() {
|
|
ws.isMouseEnabled = false
|
|
}
|
|
|
|
func (ws *Workspace) modeChange(args []interface{}) {
|
|
arg := args[0].([]interface{})
|
|
ws.modeEnablingIME(arg[0].(string))
|
|
ws.mode = arg[0].(string)
|
|
ws.modeIdx = util.ReflectToInt(arg[1])
|
|
if ws.cursor.modeIdx != ws.modeIdx {
|
|
ws.cursor.modeIdx = ws.modeIdx
|
|
}
|
|
ws.disableImeInNormal()
|
|
ws.shouldUpdate.cursor = true
|
|
}
|
|
|
|
func (ws *Workspace) winViewport(args []interface{}) {
|
|
// smooth scroll feature disabled
|
|
if !editor.config.Editor.SmoothScroll {
|
|
return
|
|
}
|
|
|
|
// Suppress smooth scroll rendering when key auto-repeat is enabled
|
|
if editor.isKeyAutoRepeating {
|
|
return
|
|
}
|
|
|
|
for _, e := range args {
|
|
arg := e.([]interface{})
|
|
|
|
grid := util.ReflectToInt(arg[0])
|
|
top := util.ReflectToInt(arg[2]) + 1
|
|
bottom := util.ReflectToInt(arg[3]) + 1
|
|
curLine := util.ReflectToInt(arg[4]) + 1
|
|
curCol := util.ReflectToInt(arg[5]) + 1
|
|
viewport := [5]int{
|
|
top,
|
|
bottom,
|
|
curLine,
|
|
curCol,
|
|
grid,
|
|
}
|
|
|
|
// fmt.Println(
|
|
// fmt.Sprintf("top:%d, bottom:%d", top, bottom),
|
|
// )
|
|
|
|
maxLine := 0
|
|
if len(arg) >= 7 {
|
|
maxLine = util.ReflectToInt(arg[6])
|
|
}
|
|
|
|
delta := -1
|
|
if len(arg) >= 8 {
|
|
delta = util.ReflectToInt(arg[7])
|
|
}
|
|
|
|
// Only the viewport of the buffer where the cursor is located is used internally.
|
|
if grid == ws.cursor.gridid {
|
|
ws.viewportMutex.Lock()
|
|
ws.oldViewport = ws.viewport
|
|
ws.viewport = viewport
|
|
ws.viewportMutex.Unlock()
|
|
ws.maxLineDelta = maxLine - ws.maxLine
|
|
ws.maxLine = maxLine
|
|
}
|
|
|
|
if delta == 0 {
|
|
continue
|
|
}
|
|
|
|
// do not scroll smoothly when the maximum line is less than buffer rows
|
|
if ws.maxLine-ws.maxLineDelta < ws.rows {
|
|
continue
|
|
}
|
|
|
|
// Does not scroll smoothly if the size of the grid is increased without
|
|
// changing the position of the top
|
|
if top == ws.oldViewport[0] && bottom != ws.oldViewport[1] {
|
|
continue
|
|
}
|
|
|
|
win, ok := ws.screen.getWindow(grid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// if grid is message grid or global grid
|
|
if win.isMsgGrid || win.grid == 1 {
|
|
continue
|
|
}
|
|
|
|
// Compatibility of smooth scrolling with touchpad and smooth scrolling with scroll commands
|
|
if win.lastScrollphase != core.Qt__ScrollEnd {
|
|
continue
|
|
}
|
|
|
|
// Suppresses smooth scrolling by command while touchpad scrolling is in progress
|
|
if win.scrollPixels[1] != 0 || win.lastScrollphase != core.Qt__ScrollEnd {
|
|
continue
|
|
}
|
|
|
|
// If the mouse is off in terminal mode and the cursor column is 0,
|
|
// it is assumed that tig, lazygit, or other proprietary UI has been executed.
|
|
if ws.isTerminalMode && ws.cursor.isBusy && curCol == 1 {
|
|
win.dropScreenSnapshot()
|
|
continue
|
|
}
|
|
|
|
if delta > 0 && delta > win.rows {
|
|
delta = 1
|
|
win.dropScreenSnapshot()
|
|
}
|
|
if delta < 0 && delta*-1 > win.rows {
|
|
delta = -1
|
|
win.dropScreenSnapshot()
|
|
}
|
|
|
|
win.smoothScroll(float64(delta))
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) winViewportMargins(args []interface{}) {
|
|
for _, e := range args {
|
|
arg := e.([]interface{})
|
|
|
|
grid := util.ReflectToInt(arg[0])
|
|
top := util.ReflectToInt(arg[2])
|
|
bottom := util.ReflectToInt(arg[3])
|
|
left := util.ReflectToInt(arg[4])
|
|
right := util.ReflectToInt(arg[5])
|
|
|
|
win, ok := ws.screen.getWindow(grid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
win.viewportMargins = [4]int{top, bottom, left, right}
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) scrollMinimap() {
|
|
absMapTop := ws.minimap.viewport[0]
|
|
absMapBottom := ws.minimap.viewport[1]
|
|
|
|
ws.viewportMutex.RLock()
|
|
topLine := ws.viewport[0]
|
|
botLine := ws.viewport[1]
|
|
currLine := ws.viewport[2]
|
|
ws.viewportMutex.RUnlock()
|
|
|
|
switch {
|
|
case botLine > absMapBottom:
|
|
ws.minimap.nvim.Input(`<ScrollWheelDown>`)
|
|
ws.minimap.nvim.Command(fmt.Sprintf("call cursor(%d, %d)", currLine, 0))
|
|
ws.minimap.nvim.Input(`zz`)
|
|
case absMapTop > topLine:
|
|
ws.minimap.nvim.Input(`<ScrollWheelUp>`)
|
|
ws.minimap.nvim.Command(fmt.Sprintf("call cursor(%d, %d)", currLine, 0))
|
|
ws.minimap.nvim.Input(`zz`)
|
|
default:
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) handleGui(updates []interface{}) {
|
|
event := updates[0].(string)
|
|
switch event {
|
|
case "gonvim_vimenter":
|
|
// openingFile := getOpeningFilePath()
|
|
// if openingFile != "" {
|
|
// go ws.nvim.Command(fmt.Sprintf(":e %s", openingFile))
|
|
// }
|
|
case "gonvim_uienter":
|
|
case "gonvim_resize":
|
|
width, height := editor.setWindowSize(updates[1].(string))
|
|
editor.window.Resize2(width, height)
|
|
case "gonvim_fullscreen":
|
|
arg := 1
|
|
if len(updates) == 2 {
|
|
arg = util.ReflectToInt(updates[1])
|
|
}
|
|
if arg == 0 {
|
|
// On MacOS, exiting from fullscreen does not work properly
|
|
// unless the window is fullscreened again beforehand.
|
|
if runtime.GOOS == "darwin" {
|
|
editor.window.WindowFullScreen()
|
|
}
|
|
editor.window.WindowExitFullScreen()
|
|
if runtime.GOOS == "darwin" && editor.savedGeometry != nil && editor.config.Editor.BorderlessWindow {
|
|
editor.window.RestoreGeometry(editor.savedGeometry)
|
|
}
|
|
} else {
|
|
if runtime.GOOS == "darwin" && editor.config.Editor.BorderlessWindow {
|
|
editor.savedGeometry = editor.window.SaveGeometry()
|
|
}
|
|
editor.window.WindowFullScreen()
|
|
}
|
|
case "gonvim_maximize":
|
|
arg := 1
|
|
if len(updates) == 2 {
|
|
arg = util.ReflectToInt(updates[1])
|
|
}
|
|
if arg == 0 {
|
|
editor.window.WindowExitMaximize()
|
|
} else {
|
|
editor.window.WindowMaximize()
|
|
}
|
|
case "gonvim_winpos":
|
|
if len(updates) == 3 {
|
|
x, ok_x := strconv.Atoi(updates[1].(string))
|
|
y, ok_y := strconv.Atoi(updates[2].(string))
|
|
if ok_x == nil && ok_y == nil {
|
|
newPos := core.NewQPoint2(x, y)
|
|
editor.window.Move(newPos)
|
|
}
|
|
}
|
|
case "gonvim_toggle_horizontal_scroll":
|
|
if editor.config.Editor.DisableHorizontalScroll {
|
|
editor.config.Editor.DisableHorizontalScroll = false
|
|
} else {
|
|
editor.config.Editor.DisableHorizontalScroll = true
|
|
}
|
|
|
|
case "gonvim_smoothscroll":
|
|
ws.toggleSmoothScroll()
|
|
case "gonvim_smoothcursor":
|
|
ws.toggleSmoothCursor()
|
|
case "gonvim_indentguide":
|
|
ws.toggleIndentguide()
|
|
case "gonvim_ligatures":
|
|
ws.toggleLigatures()
|
|
case "gonvim_mousescroll_unit":
|
|
ws.setMousescrollUnit(updates[1].(string))
|
|
case "Font":
|
|
ws.guiFont(updates[1].(string))
|
|
case "Linespace":
|
|
ws.guiLinespace(updates[1])
|
|
// case "finder_pattern":
|
|
// ws.finder.showPattern(updates[1:])
|
|
// case "finder_pattern_pos":
|
|
// ws.finder.cursorPos(updates[1:])
|
|
// case "finder_show_result":
|
|
// ws.finder.showResult(updates[1:])
|
|
// case "finder_show":
|
|
// ws.finder.show()
|
|
// case "finder_hide":
|
|
// ws.finder.hide()
|
|
// case "finder_select":
|
|
// ws.finder.selectResult(updates[1:])
|
|
// case "signature_show":
|
|
// ws.signature.showItem(updates[1:])
|
|
// case "signature_pos":
|
|
// ws.signature.pos(updates[1:])
|
|
// case "signature_hide":
|
|
// ws.signature.hide()
|
|
case "side_open":
|
|
editor.side.show()
|
|
case "side_close":
|
|
editor.side.hide()
|
|
case "side_toggle":
|
|
editor.side.toggle()
|
|
ws.updateSize()
|
|
case "filer_update":
|
|
if !editor.side.scrollarea.IsVisible() {
|
|
return
|
|
}
|
|
if !editor.side.items[editor.active].isContentHide {
|
|
go ws.nvim.Call("rpcnotify", nil, 0, "GonvimFiler", "redraw")
|
|
}
|
|
case "filer_open":
|
|
editor.side.items[ws.getNum()].isContentHide = false
|
|
editor.side.items[ws.getNum()].openContent()
|
|
case "filer_clear":
|
|
editor.side.items[ws.getNum()].clear()
|
|
case "filer_resize":
|
|
editor.side.items[ws.getNum()].resizeContent()
|
|
case "filer_item_add":
|
|
editor.side.items[ws.getNum()].addItem(updates[1:])
|
|
case "filer_item_select":
|
|
editor.side.items[ws.getNum()].selectItem(updates[1:])
|
|
case "gonvim_letter_spacing":
|
|
ws.letterSpacing(updates[1])
|
|
case "gonvim_grid_font":
|
|
ws.screen.gridFont(updates[1])
|
|
case "gonvim_macmeta":
|
|
ws.handleMacmeta(updates[1])
|
|
case "gonvim_minimap_update":
|
|
if ws.minimap != nil {
|
|
if ws.minimap.visible {
|
|
ws.minimap.bufUpdate()
|
|
}
|
|
}
|
|
case "gonvim_minimap_sync":
|
|
if ws.minimap != nil {
|
|
if ws.minimap.visible {
|
|
ws.minimap.bufSync()
|
|
}
|
|
}
|
|
case "gonvim_minimap_toggle":
|
|
ws.minimap.toggle()
|
|
case "gonvim_colorscheme":
|
|
if ws.minimap != nil {
|
|
ws.minimap.isSetColorscheme = false
|
|
ws.minimap.setColorscheme()
|
|
}
|
|
|
|
win, ok := ws.screen.getWindow(ws.cursor.gridid)
|
|
if !ok {
|
|
return
|
|
}
|
|
win.dropScreenSnapshot()
|
|
|
|
case "gonvim_workspace_new":
|
|
editor.workspaceAdd()
|
|
case "gonvim_workspace_next":
|
|
editor.workspaceNext()
|
|
case "gonvim_workspace_previous":
|
|
editor.workspacePrevious()
|
|
case "gonvim_workspace_switch":
|
|
editor.workspaceSwitch(util.ReflectToInt(updates[1]))
|
|
case "gonvim_workspace_cwd":
|
|
cwdinfo := updates[1].(map[string]interface{})
|
|
ws.handleChangeCwd(cwdinfo)
|
|
case "gonvim_workspace_filepath":
|
|
if ws.minimap != nil {
|
|
ws.minimap.mu.Lock()
|
|
ws.filepath = updates[1].(string)
|
|
ws.minimap.mu.Unlock()
|
|
}
|
|
case "gonvim_termenter":
|
|
ws.isTerminalMode = true
|
|
ws.modeEnablingIME(ws.mode)
|
|
case "gonvim_termleave":
|
|
ws.isTerminalMode = false
|
|
ws.modeEnablingIME(ws.mode)
|
|
case "gonvim_bufenter":
|
|
wid := (nvim.Window)(util.ReflectToInt(updates[1]))
|
|
|
|
win, ok := ws.screen.getGrid(wid)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if editor.config.Editor.IndentGuide {
|
|
// get tabstop
|
|
win.ts = util.ReflectToInt(
|
|
ws.getBufferOption(NVIMCALLTIMEOUT, editor.config.Editor.OptionsToUseGuideWidth, wid),
|
|
)
|
|
|
|
// get filetype
|
|
ftITF := ws.getBufferOption(NVIMCALLTIMEOUT, "filetype", wid)
|
|
ft, ok := ftITF.(string)
|
|
if !ok {
|
|
return
|
|
}
|
|
win.ft = ft
|
|
}
|
|
|
|
// get winbar
|
|
if win.winbar != nil {
|
|
winbar := ws.getWindowOption(NVIMCALLTIMEOUT2, "winbar", "local", wid)
|
|
win.winbar = &winbar
|
|
}
|
|
|
|
case "gonvim_optionset":
|
|
wid := (nvim.Window)(util.ReflectToInt(updates[4]))
|
|
win, ok := ws.screen.getGrid(wid)
|
|
if !ok {
|
|
return
|
|
}
|
|
if win.lastScrollphase != core.Qt__ScrollEnd {
|
|
return
|
|
}
|
|
|
|
optionName, ok := updates[1].(string)
|
|
if !ok {
|
|
return
|
|
}
|
|
ws.setOption(optionName, wid)
|
|
|
|
default:
|
|
fmt.Println("unhandled Gui event", event)
|
|
}
|
|
|
|
}
|
|
|
|
func (ws *Workspace) getSnapshot() {
|
|
if !editor.config.Editor.SmoothScroll {
|
|
return
|
|
}
|
|
win, ok := ws.screen.getWindow(ws.cursor.gridid)
|
|
if !ok {
|
|
return
|
|
}
|
|
win.grabScreenSnapshot()
|
|
}
|
|
|
|
func (ws *Workspace) setMousescrollUnit(ms string) {
|
|
if !(ms == "line" || ms == "smart" || ms == "pixel") {
|
|
editor.config.Editor.MouseScrollingUnit = "line"
|
|
return
|
|
}
|
|
editor.config.Editor.MouseScrollingUnit = ms
|
|
}
|
|
|
|
func (ws *Workspace) letterSpacing(arg interface{}) {
|
|
if arg == "" {
|
|
return
|
|
}
|
|
|
|
letterSpace := util.ReflectToInt(arg)
|
|
editor.config.Editor.Letterspace = letterSpace
|
|
|
|
ws.screen.font.changeLetterSpace(letterSpace)
|
|
for _, font := range ws.screen.fallbackfonts {
|
|
font.changeLetterSpace(letterSpace)
|
|
}
|
|
|
|
font := ws.screen.font
|
|
fallbackfonts := ws.screen.fallbackfonts
|
|
|
|
win, ok := ws.screen.getWindow(ws.cursor.gridid)
|
|
if ok {
|
|
font = win.getFont()
|
|
}
|
|
|
|
ws.updateSize()
|
|
|
|
if ws.popup != nil {
|
|
ws.popup.updateFont(font)
|
|
}
|
|
if ws.message != nil {
|
|
ws.message.updateFont()
|
|
}
|
|
ws.screen.tooltip.setFont(font)
|
|
ws.cursor.updateFont(nil, font, fallbackfonts)
|
|
}
|
|
|
|
func (ws *Workspace) handleFontDialog(guifontStr string, args string) {
|
|
if ws.fontdialog == nil {
|
|
fDialog := widgets.NewQFontDialog(nil)
|
|
fDialog.SetOption(widgets.QFontDialog__MonospacedFonts, true)
|
|
fDialog.SetOption(widgets.QFontDialog__ProportionalFonts, false)
|
|
fDialog.ConnectFontSelected(func(font *gui.QFont) {
|
|
ff := strings.Replace(font.Family(), " ", "_", -1)
|
|
fh := font.PointSizeF()
|
|
editor.putLog(fmt.Sprintf("Request to change to the following font:: %s:h%f", ff, fh))
|
|
|
|
// Fix the problem that the value of echo &guifont is set to * after setting.
|
|
// ws.guiFont(fmt.Sprintf("%s:h%f", fontFamily, fontHeight))
|
|
ws.nvim.Command(fmt.Sprintf("set %s=%s:h%f", guifontStr, ff, fh))
|
|
})
|
|
ws.fontdialog = fDialog
|
|
}
|
|
ws.fontdialog.Show()
|
|
}
|
|
|
|
func (ws *Workspace) guiFont(args string) {
|
|
if args == "" {
|
|
return
|
|
}
|
|
|
|
editor.bindResizeEvent()
|
|
|
|
if args == "*" {
|
|
ws.handleFontDialog("guifont", args)
|
|
return
|
|
}
|
|
|
|
ws.screen.fallbackfonts = nil
|
|
|
|
ws.parseAndApplyFont(args, &ws.screen.font, &ws.screen.fallbackfonts)
|
|
ws.screen.purgeTextCacheForWins()
|
|
|
|
// When setting up a different font for a workspace other than the neovim drawing screen,
|
|
// it is necessary to consider handling the fonts on the workspace side independently, etc.
|
|
ws.font = ws.screen.font
|
|
|
|
font := ws.screen.font
|
|
fallbackfonts := ws.screen.fallbackfonts
|
|
|
|
win, ok := ws.screen.getWindow(ws.cursor.gridid)
|
|
if ok {
|
|
font = win.getFont()
|
|
}
|
|
|
|
ws.updateSize()
|
|
|
|
editor.iconSize = int(float64(ws.screen.font.height) * 11 / 9)
|
|
|
|
if ws.popup != nil {
|
|
ws.popup.updateFont(font)
|
|
}
|
|
if ws.message != nil {
|
|
ws.message.updateFont()
|
|
}
|
|
|
|
ws.screen.tooltip.setFont(font)
|
|
ws.screen.tooltip.fallbackfonts = fallbackfonts
|
|
|
|
ws.cursor.updateFont(nil, font, fallbackfonts)
|
|
ws.cursor.fallbackfonts = fallbackfonts
|
|
|
|
// TODO:
|
|
// Consideration of application UI policies related to external and Neovim internal fonts,
|
|
// and provide a way to change external fonts.
|
|
|
|
if ws.tabline != nil {
|
|
ws.tabline.updateFont()
|
|
}
|
|
}
|
|
|
|
func (ws *Workspace) guiFontWide(args string) {
|
|
if args == "" {
|
|
return
|
|
}
|
|
|
|
if args == "*" {
|
|
ws.handleFontDialog("guifontwide", args)
|
|
return
|
|
}
|
|
|
|
ws.screen.fallbackfontwides = nil
|
|
|
|
ws.parseAndApplyFont(args, &ws.screen.fontwide, &ws.screen.fallbackfontwides)
|
|
ws.screen.purgeTextCacheForWins()
|
|
|
|
ws.updateSize()
|
|
}
|
|
|
|
func (ws *Workspace) parseAndApplyFont(str string, font *(*Font), fonts *([]*Font)) {
|
|
errGfns := []string{}
|
|
for i, gfn := range strings.Split(str, ",") {
|
|
fontFamily, fontHeight, fontWeight, fontStretch := getFontFamilyAndHeightAndWeightAndStretch(gfn)
|
|
|
|
ok := checkValidFont(fontFamily)
|
|
if !ok {
|
|
errGfns = append(errGfns, fontFamily)
|
|
continue
|
|
}
|
|
|
|
if i == 0 {
|
|
if *font == nil {
|
|
*font = initFontNew(
|
|
fontFamily,
|
|
fontHeight,
|
|
fontWeight,
|
|
fontStretch,
|
|
ws.screen.font.lineSpace,
|
|
ws.screen.font.letterSpace,
|
|
)
|
|
} else {
|
|
(*font).change(fontFamily, fontHeight, fontWeight, fontStretch)
|
|
}
|
|
} else {
|
|
ff := initFontNew(
|
|
fontFamily,
|
|
fontHeight,
|
|
fontWeight,
|
|
fontStretch,
|
|
(*font).lineSpace,
|
|
(*font).letterSpace,
|
|
)
|
|
*fonts = append(*fonts, ff)
|
|
}
|
|
}
|
|
|
|
if len(errGfns) > 0 {
|
|
s := ""
|
|
for k, errgfn := range errGfns {
|
|
s += "'" + errgfn + "'"
|
|
if k < len(errGfns)-1 {
|
|
s += ", "
|
|
}
|
|
}
|
|
editor.pushNotification(
|
|
NotifyWarn,
|
|
4,
|
|
fmt.Sprintf("The specified font family %s was not found on this system.", s),
|
|
notifyOptionArg([]*NotifyButton{}),
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
func getFontFamilyAndHeightAndWeightAndStretch(s string) (string, float64, gui.QFont__Weight, int) {
|
|
parts := strings.Split(s, ":")
|
|
height := -1.0
|
|
width := -1.0
|
|
weight := gui.QFont__Normal
|
|
if len(parts) > 1 {
|
|
for _, p := range parts[1:] {
|
|
if strings.HasPrefix(p, "h") {
|
|
// height, err = strconv.Atoi(p[1:])
|
|
h, err := strconv.ParseFloat(p[1:], 64)
|
|
if err == nil {
|
|
height = h
|
|
}
|
|
} else if strings.HasPrefix(p, "w") {
|
|
// width, err := strconv.Atoi(p[1:])
|
|
w, err := strconv.ParseFloat(p[1:], 64)
|
|
if err == nil {
|
|
width = w
|
|
}
|
|
} else if p == "t" {
|
|
weight = gui.QFont__Thin
|
|
} else if p == "el" {
|
|
weight = gui.QFont__ExtraLight
|
|
} else if p == "l" {
|
|
weight = gui.QFont__Light
|
|
} else if p == "n" {
|
|
// default weight, we do nothing
|
|
} else if p == "db" || p == "sb" {
|
|
weight = gui.QFont__DemiBold
|
|
} else if p == "b" {
|
|
weight = gui.QFont__Bold
|
|
} else if p == "eb" {
|
|
weight = gui.QFont__ExtraBold
|
|
} else {
|
|
weight = gui.QFont__Normal
|
|
}
|
|
}
|
|
}
|
|
// A '_' can be used in the place of a space, so you don't need to use
|
|
// backslashes to escape the spaces.
|
|
family := strings.Replace(parts[0], "_", " ", -1)
|
|
|
|
if height <= 1.0 && width <= 0 {
|
|
height = 12
|
|
width = 6
|
|
} else if height > 1.0 && width == -1.0 {
|
|
width = height / 2.0
|
|
} else if height <= 1.0 && width >= 1.0 {
|
|
height = width * 2.0
|
|
}
|
|
|
|
stretch := int(float64(width) / float64(height) * 2.0 * 100.0)
|
|
|
|
return family, height, weight, stretch
|
|
}
|
|
|
|
func checkValidFont(family string) bool {
|
|
// f := gui.NewQFont2(family, 10.0, 1, false)
|
|
f := gui.NewQFont()
|
|
if editor.config.Editor.ManualFontFallback {
|
|
f.SetStyleHint(gui.QFont__TypeWriter, gui.QFont__NoFontMerging)
|
|
} else {
|
|
f.SetStyleHint(gui.QFont__TypeWriter, gui.QFont__PreferDefault|gui.QFont__ForceIntegerMetrics)
|
|
}
|
|
|
|
f.SetFamily(family)
|
|
f.SetPointSizeF(10.0)
|
|
f.SetWeight(int(gui.QFont__Normal))
|
|
|
|
fi := gui.NewQFontInfo(f)
|
|
|
|
fname1 := fi.Family()
|
|
fname2 := f.Family()
|
|
|
|
ret := strings.EqualFold(fname1, fname2)
|
|
|
|
return ret
|
|
}
|
|
|
|
func (ws *Workspace) guiLinespace(args interface{}) {
|
|
// fontArg := args[0].([]interface{})
|
|
var lineSpace int
|
|
var err error
|
|
switch arg := args.(type) {
|
|
case string:
|
|
lineSpace, err = strconv.Atoi(arg)
|
|
if err != nil {
|
|
return
|
|
}
|
|
case int32: // can't combine these in a type switch without compile error
|
|
lineSpace = int(arg)
|
|
case int64:
|
|
lineSpace = int(arg)
|
|
default:
|
|
return
|
|
}
|
|
|
|
// #330: From a rendering architecture perspective, specifying negative values
|
|
// may not render screen content correctly, but there is a need to set negative values,
|
|
// so there is no restriction on setting negative values.
|
|
// if lineSpace < 0 {
|
|
// return
|
|
// }
|
|
if lineSpace <= -1*ws.font.height {
|
|
return
|
|
}
|
|
|
|
ws.screen.font.changeLineSpace(lineSpace)
|
|
for _, font := range ws.screen.fallbackfonts {
|
|
font.changeLineSpace(lineSpace)
|
|
}
|
|
|
|
ws.font = ws.screen.font
|
|
ws.updateSize()
|
|
}
|
|
|
|
func (ws *Workspace) setPumblend(arg interface{}) {
|
|
var pumblend int
|
|
var err error
|
|
switch val := arg.(type) {
|
|
case string:
|
|
pumblend, err = strconv.Atoi(val)
|
|
if err != nil {
|
|
return
|
|
}
|
|
case int32: // can't combine these in a type switch without compile error
|
|
pumblend = int(val)
|
|
case int64:
|
|
pumblend = int(val)
|
|
default:
|
|
return
|
|
}
|
|
|
|
ws.pb = pumblend
|
|
ws.screen.purgeTextCacheForWins()
|
|
}
|
|
|
|
func (ws *Workspace) getWindowOption(timeout int, option, scope string, wid ...nvim.Window) string {
|
|
opts := fmt.Sprintf(
|
|
`{"scope":"%s"`,
|
|
scope,
|
|
)
|
|
if len(wid) > 0 {
|
|
opts += fmt.Sprintf(
|
|
`, "win":%d}`,
|
|
int(wid[0]),
|
|
)
|
|
} else {
|
|
opts += "}"
|
|
}
|
|
nvimGetOptionValue := fmt.Sprintf(
|
|
`echo nvim_get_option_value("%s", %s)`,
|
|
option,
|
|
opts,
|
|
)
|
|
c := make(chan string, 10)
|
|
go func() {
|
|
result, _ := ws.nvim.CommandOutput(nvimGetOptionValue)
|
|
c <- result
|
|
}()
|
|
|
|
var result string
|
|
select {
|
|
case result = <-c:
|
|
case <-time.After(time.Duration(timeout) * time.Millisecond):
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (ws *Workspace) getBuffer(wid nvim.Window) (buf nvim.Buffer) {
|
|
// get neovim buffer
|
|
bufChan := make(chan nvim.Buffer, 10)
|
|
go func() {
|
|
resultBuffer, _ := ws.nvim.WindowBuffer(wid)
|
|
bufChan <- resultBuffer
|
|
}()
|
|
select {
|
|
case buf = <-bufChan:
|
|
case <-time.After(NVIMCALLTIMEOUT * time.Millisecond):
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (ws *Workspace) getBufferOption(timeout int, option string, wid nvim.Window) interface{} {
|
|
buf := ws.getBuffer(wid)
|
|
|
|
// get buffer tabstop
|
|
c := make(chan interface{}, 5)
|
|
go func() {
|
|
var result interface{}
|
|
ws.nvim.BufferOption(buf, option, &result)
|
|
c <- result
|
|
}()
|
|
|
|
var result interface{}
|
|
select {
|
|
case result = <-c:
|
|
case <-time.After(time.Duration(timeout) * time.Millisecond):
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// setOption is
|
|
// This function gets the value of an option that cannot be caught by the set_option event.
|
|
func (ws *Workspace) setOption(optionName string, wid nvim.Window) {
|
|
win, ok := ws.screen.getGrid(wid)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
ws.optionsetMutex.Lock()
|
|
switch optionName {
|
|
case editor.config.Editor.OptionsToUseGuideWidth:
|
|
win.ts = util.ReflectToInt(
|
|
ws.getBufferOption(NVIMCALLTIMEOUT, optionName, wid),
|
|
)
|
|
case "filetype":
|
|
ftITF := ws.getBufferOption(NVIMCALLTIMEOUT, optionName, wid)
|
|
ft, ok := ftITF.(string)
|
|
if !ok {
|
|
return
|
|
}
|
|
win.ft = ft
|
|
case "winbar":
|
|
|
|
// for global-local
|
|
winbar := ws.getWindowOption(NVIMCALLTIMEOUT2, optionName, "global")
|
|
ws.winbar = &winbar
|
|
|
|
// for window-local
|
|
winbar = ws.getWindowOption(NVIMCALLTIMEOUT2, optionName, "local", wid)
|
|
win.winbar = &winbar
|
|
}
|
|
ws.optionsetMutex.Unlock()
|
|
}
|
|
|
|
// InputMethodEvent is
|
|
func (ws *Workspace) InputMethodEvent(event *gui.QInputMethodEvent) {
|
|
ws.screen.tooltip.cursorPos, ws.screen.tooltip.selectionLength = selectionPosInPreeditStr(event)
|
|
|
|
if event.CommitString() != "" {
|
|
ws.screen.tooltip.cursorVisualPos = 0
|
|
ws.nvim.Input(event.CommitString())
|
|
ws.screen.tooltip.hide()
|
|
ws.screen.tooltip.clearText()
|
|
} else {
|
|
preeditString := event.PreeditString()
|
|
|
|
if preeditString == "" {
|
|
ws.screen.tooltip.hide()
|
|
ws.screen.refresh()
|
|
} else {
|
|
ws.screen.tooltip.show()
|
|
ws.screen.tooltip.parsePreeditString(preeditString)
|
|
ws.screen.tooltip.update()
|
|
|
|
}
|
|
|
|
ws.screen.tooltip.updateVirtualCursorPos()
|
|
}
|
|
|
|
ws.cursor.update()
|
|
|
|
editor.putLog(
|
|
fmt.Sprintf(
|
|
"QInputMethodEvent:: IME preeditstr: cursorpos: %d, selectionLength: %d, cursorVisualPos: %d",
|
|
ws.screen.tooltip.cursorPos,
|
|
ws.screen.tooltip.selectionLength,
|
|
ws.screen.tooltip.cursorVisualPos,
|
|
),
|
|
)
|
|
}
|
|
|
|
// InputMethodQuery is
|
|
func (ws *Workspace) InputMethodQuery(query core.Qt__InputMethodQuery) *core.QVariant {
|
|
if ws.screen == nil {
|
|
return core.NewQVariant()
|
|
}
|
|
if ws.screen.tooltip == nil {
|
|
return core.NewQVariant()
|
|
}
|
|
if !ws.screen.tooltip.isShown {
|
|
return core.NewQVariant()
|
|
}
|
|
|
|
editor.putLog(
|
|
fmt.Sprintf(
|
|
"InputMethodQuery:: query: %d", query,
|
|
),
|
|
)
|
|
|
|
if query == core.Qt__ImMicroFocus || query == core.Qt__ImCursorRectangle {
|
|
x, y, candX, candY := ws.screen.tooltip.pos()
|
|
ws.screen.tooltip.move(x, y)
|
|
imrect := core.NewQRect()
|
|
|
|
res := 0
|
|
win, ok := ws.screen.getWindow(ws.cursor.gridid)
|
|
if ok {
|
|
if win.isMsgGrid {
|
|
res = win.s.widget.Height() - win.rows*ws.font.lineHeight
|
|
}
|
|
if res < 0 {
|
|
res = 0
|
|
}
|
|
}
|
|
imrect.SetRect(candX, candY+res+5, 1, ws.font.lineHeight)
|
|
|
|
return core.NewQVariant31(imrect)
|
|
}
|
|
|
|
return core.NewQVariant()
|
|
}
|
|
|
|
func (ws *Workspace) dragEnterEvent(e *gui.QDragEnterEvent) {
|
|
e.AcceptProposedAction()
|
|
}
|
|
|
|
func (ws *Workspace) dragMoveEvent(e *gui.QDragMoveEvent) {
|
|
e.AcceptProposedAction()
|
|
}
|
|
|
|
func (ws *Workspace) dropEvent(e *gui.QDropEvent) {
|
|
e.SetDropAction(core.Qt__CopyAction)
|
|
e.AcceptProposedAction()
|
|
e.SetAccepted(true)
|
|
|
|
ws.screen.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
win.move(win.pos[0], win.pos[1])
|
|
}
|
|
if win.isExternal {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
|
|
if win.Geometry().Contains(e.Pos(), true) {
|
|
win.DropEvent(e)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (ws *Workspace) getPointInWidget(col, row, grid int) (int, int, *Font, bool) {
|
|
win, ok := ws.screen.getWindow(grid)
|
|
if !ok {
|
|
return 0, 0, ws.font, false
|
|
}
|
|
font := win.getFont()
|
|
|
|
isCursorBelowTheCenter := false
|
|
if (win.pos[1]+row)*font.lineHeight > ws.screen.height/2 {
|
|
isCursorBelowTheCenter = true
|
|
}
|
|
|
|
x := int(float64(col) * font.cellwidth)
|
|
y := row * font.lineHeight
|
|
if ws.isDrawTabline {
|
|
if ws.tabline != nil {
|
|
y += ws.tabline.widget.Height()
|
|
}
|
|
}
|
|
x += int(float64(win.pos[0]) * font.cellwidth)
|
|
y += win.pos[1] * font.lineHeight
|
|
|
|
return x, y, font, isCursorBelowTheCenter
|
|
}
|
|
|
|
func (ws *Workspace) toggleSmoothScroll() {
|
|
editor.config.mu.Lock()
|
|
if editor.config.Editor.SmoothScroll {
|
|
editor.config.Editor.SmoothScroll = false
|
|
} else {
|
|
editor.config.Editor.SmoothScroll = true
|
|
}
|
|
editor.config.mu.Unlock()
|
|
}
|
|
|
|
func (ws *Workspace) toggleSmoothCursor() {
|
|
editor.config.mu.Lock()
|
|
if editor.config.Cursor.SmoothMove {
|
|
editor.config.Cursor.SmoothMove = false
|
|
} else {
|
|
editor.config.Cursor.SmoothMove = true
|
|
}
|
|
ws.cursor.hasSmoothMove = editor.config.Cursor.SmoothMove
|
|
editor.config.mu.Unlock()
|
|
}
|
|
|
|
func (ws *Workspace) handleMacmeta(v interface{}) {
|
|
value := util.ReflectToInt(v)
|
|
editor.config.mu.Lock()
|
|
if value == 0 {
|
|
editor.config.Editor.Macmeta = false
|
|
} else {
|
|
editor.config.Editor.Macmeta = true
|
|
}
|
|
editor.config.mu.Unlock()
|
|
}
|
|
|
|
func (ws *Workspace) toggleLigatures() {
|
|
editor.config.mu.Lock()
|
|
if editor.config.Editor.DisableLigatures {
|
|
editor.config.Editor.DisableLigatures = false
|
|
editor.config.Editor.Letterspace = 0
|
|
} else {
|
|
editor.config.Editor.DisableLigatures = true
|
|
}
|
|
editor.config.mu.Unlock()
|
|
|
|
ws.screen.purgeTextCacheForWins()
|
|
}
|
|
|
|
func (ws *Workspace) toggleIndentguide() {
|
|
editor.config.mu.Lock()
|
|
if editor.config.Editor.IndentGuide {
|
|
editor.config.Editor.IndentGuide = false
|
|
} else {
|
|
editor.config.Editor.IndentGuide = true
|
|
}
|
|
editor.config.mu.Unlock()
|
|
ws.screen.refresh()
|
|
go ws.nvim.Command("doautocmd <nomodeline> WinEnter")
|
|
}
|
|
|
|
// WorkspaceSide is
|
|
type WorkspaceSide struct {
|
|
widget *widgets.QWidget
|
|
scrollarea *widgets.QScrollArea
|
|
header *widgets.QLabel
|
|
scrollBg *RGBA
|
|
selectBg *RGBA
|
|
accent *RGBA
|
|
fg *RGBA
|
|
sfg *RGBA
|
|
scrollFg *RGBA
|
|
items []*WorkspaceSideItem
|
|
isShown bool
|
|
isInitResize bool
|
|
}
|
|
|
|
func newWorkspaceSide() *WorkspaceSide {
|
|
layout := util.NewHFlowLayout(0, 0, 0, 0, 20)
|
|
layout.SetContentsMargins(0, 0, 0, 0)
|
|
layout.SetSpacing(0)
|
|
header := widgets.NewQLabel(nil, 0)
|
|
header.SetContentsMargins(22, 15, 20, 10)
|
|
header.SetText("WORKSPACE")
|
|
widget := widgets.NewQWidget(nil, 0)
|
|
widget.SetContentsMargins(0, 0, 0, 100)
|
|
widget.SetLayout(layout)
|
|
widget.SetSizePolicy2(widgets.QSizePolicy__Expanding, widgets.QSizePolicy__Expanding)
|
|
|
|
// HideMouseWhenTyping process
|
|
if editor.config.Editor.HideMouseWhenTyping {
|
|
widget.InstallEventFilter(widget)
|
|
widget.SetMouseTracking(true)
|
|
}
|
|
widget.ConnectEventFilter(func(watched *core.QObject, event *core.QEvent) bool {
|
|
switch event.Type() {
|
|
case core.QEvent__MouseMove:
|
|
if editor.isHideMouse && editor.config.Editor.HideMouseWhenTyping {
|
|
gui.QGuiApplication_RestoreOverrideCursor()
|
|
editor.isHideMouse = false
|
|
}
|
|
default:
|
|
}
|
|
|
|
return widget.EventFilterDefault(watched, event)
|
|
})
|
|
|
|
side := &WorkspaceSide{
|
|
widget: widget,
|
|
header: header,
|
|
}
|
|
|
|
layout.AddWidget(header)
|
|
side.header.Show()
|
|
|
|
items := []*WorkspaceSideItem{}
|
|
side.items = items
|
|
for i := 0; i < WORKSPACELEN; i++ {
|
|
item := newWorkspaceSideItem()
|
|
side.items = append(side.items, item)
|
|
side.items[len(side.items)-1].side = side
|
|
layout.AddWidget(side.items[len(side.items)-1].widget)
|
|
side.items[len(side.items)-1].hide()
|
|
}
|
|
|
|
return side
|
|
}
|
|
|
|
func (side *WorkspaceSide) newScrollArea() {
|
|
sideArea := widgets.NewQScrollArea(nil)
|
|
sideArea.SetWidgetResizable(true)
|
|
sideArea.SetVerticalScrollBarPolicy(core.Qt__ScrollBarAlwaysOff)
|
|
sideArea.ConnectEnterEvent(func(event *core.QEvent) {
|
|
sideArea.SetVerticalScrollBarPolicy(core.Qt__ScrollBarAsNeeded)
|
|
})
|
|
sideArea.ConnectLeaveEvent(func(event *core.QEvent) {
|
|
sideArea.SetVerticalScrollBarPolicy(core.Qt__ScrollBarAlwaysOff)
|
|
})
|
|
sideArea.SetFocusPolicy(core.Qt__NoFocus | core.Qt__ClickFocus)
|
|
sideArea.SetFrameShape(widgets.QFrame__NoFrame)
|
|
// sideArea.SetFixedWidth(editor.config.SideBar.Width)
|
|
|
|
side.scrollarea = sideArea
|
|
|
|
side.scrollarea.ConnectResizeEvent(func(*gui.QResizeEvent) {
|
|
width := side.scrollarea.Width()
|
|
for _, item := range side.items {
|
|
item.label.SetMaximumWidth(width)
|
|
item.label.SetMinimumWidth(width)
|
|
item.content.SetMinimumWidth(width)
|
|
item.content.SetMinimumWidth(width)
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
func (side *WorkspaceSide) toggle() {
|
|
if side == nil {
|
|
return
|
|
}
|
|
if side.isShown {
|
|
side.hide()
|
|
} else {
|
|
side.show()
|
|
}
|
|
}
|
|
|
|
func (side *WorkspaceSide) show() {
|
|
if side == nil {
|
|
return
|
|
}
|
|
side.setColor()
|
|
if side.isShown {
|
|
return
|
|
}
|
|
if !side.isInitResize {
|
|
editor.splitter.SetSizes(
|
|
[]int{editor.config.SideBar.Width,
|
|
editor.width - editor.config.SideBar.Width},
|
|
)
|
|
side.isInitResize = true
|
|
}
|
|
side.scrollarea.Show()
|
|
side.isShown = true
|
|
|
|
for i := 0; i < WORKSPACELEN; i++ {
|
|
if i >= len(editor.workspaces) {
|
|
break
|
|
}
|
|
if side.items[i] == nil {
|
|
continue
|
|
}
|
|
// if !side.items[i].active {
|
|
// continue
|
|
// }
|
|
if editor.workspaces[i] != nil {
|
|
if side.items[i].label.Text() == "" {
|
|
editor.workspaces[i].setCwd(editor.workspaces[i].cwdlabel)
|
|
}
|
|
}
|
|
side.items[i].setSideItemLabel(i)
|
|
side.items[i].show()
|
|
editor.workspaces[i].hide()
|
|
if i == editor.active {
|
|
editor.workspaces[i].show()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (side *WorkspaceSide) hide() {
|
|
if side == nil {
|
|
return
|
|
}
|
|
if editor.config.SideBar.Visible {
|
|
return
|
|
}
|
|
if !side.isShown {
|
|
return
|
|
}
|
|
side.scrollarea.Hide()
|
|
side.isShown = false
|
|
}
|
|
|
|
func (ws *Workspace) getNum() int {
|
|
for i, wse := range editor.workspaces {
|
|
if ws == wse {
|
|
return i
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// WorkspaceSideItem is
|
|
type WorkspaceSideItem struct {
|
|
label *widgets.QLabel
|
|
content *widgets.QListWidget
|
|
side *WorkspaceSide
|
|
openIcon *svg.QSvgWidget
|
|
closeIcon *svg.QSvgWidget
|
|
widget *widgets.QWidget
|
|
layout *widgets.QBoxLayout
|
|
labelWidget *widgets.QWidget
|
|
text string
|
|
cwdpath string
|
|
hidden bool
|
|
active bool
|
|
isContentHide bool
|
|
}
|
|
|
|
func newWorkspaceSideItem() *WorkspaceSideItem {
|
|
widget := widgets.NewQWidget(nil, 0)
|
|
widget.SetStyleSheet(" * { background-color: rgba(0, 0, 0, 0); }")
|
|
|
|
layout := widgets.NewQBoxLayout(widgets.QBoxLayout__TopToBottom, widget)
|
|
layout.SetContentsMargins(0, 5, 0, 5)
|
|
|
|
labelWidget := widgets.NewQWidget(nil, 0)
|
|
labelLayout := widgets.NewQHBoxLayout()
|
|
labelWidget.SetLayout(labelLayout)
|
|
labelWidget.SetSizePolicy2(widgets.QSizePolicy__Expanding, widgets.QSizePolicy__Expanding)
|
|
labelLayout.SetContentsMargins(15, 1, 1, 1)
|
|
labelLayout.SetSpacing(editor.iconSize / 2)
|
|
|
|
label := widgets.NewQLabel(nil, 0)
|
|
label.SetContentsMargins(0, 0, 0, 0)
|
|
label.SetAlignment(core.Qt__AlignLeft)
|
|
width := editor.config.SideBar.Width
|
|
label.SetMaximumWidth(width)
|
|
label.SetMinimumWidth(width)
|
|
|
|
openIcon := svg.NewQSvgWidget(nil)
|
|
openIcon.SetFixedWidth(editor.iconSize - 1)
|
|
openIcon.SetFixedHeight(editor.iconSize - 1)
|
|
svgContent := editor.getSvg("chevron-down", nil)
|
|
openIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent)))
|
|
|
|
closeIcon := svg.NewQSvgWidget(nil)
|
|
closeIcon.SetFixedWidth(editor.iconSize - 1)
|
|
closeIcon.SetFixedHeight(editor.iconSize - 1)
|
|
svgContent = editor.getSvg("chevron-right", nil)
|
|
closeIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent)))
|
|
|
|
content := widgets.NewQListWidget(nil)
|
|
content.SetFocusPolicy(core.Qt__NoFocus)
|
|
content.SetFrameShape(widgets.QFrame__NoFrame)
|
|
content.SetHorizontalScrollBarPolicy(core.Qt__ScrollBarAlwaysOff)
|
|
content.SetFont(editor.font.qfont)
|
|
content.SetIconSize(core.NewQSize2(editor.iconSize*3/4, editor.iconSize*3/4))
|
|
|
|
labelLayout.AddWidget(openIcon, 0, 0)
|
|
labelLayout.AddWidget(closeIcon, 0, 0)
|
|
labelLayout.AddWidget(label, 0, 0)
|
|
|
|
labelLayout.SetAlignment(openIcon, core.Qt__AlignLeft)
|
|
labelLayout.SetAlignment(closeIcon, core.Qt__AlignLeft)
|
|
labelLayout.SetAlignment(label, core.Qt__AlignLeft)
|
|
// layout.AddWidget(flwidget, 0, 0)
|
|
|
|
layout.AddWidget(labelWidget, 1, 0)
|
|
layout.AddWidget(content, 0, 0)
|
|
layout.SetAlignment(labelWidget, core.Qt__AlignLeft)
|
|
layout.SetAlignment(content, core.Qt__AlignLeft)
|
|
|
|
openIcon.Hide()
|
|
closeIcon.Show()
|
|
|
|
sideitem := &WorkspaceSideItem{
|
|
widget: widget,
|
|
layout: layout,
|
|
labelWidget: labelWidget,
|
|
label: label,
|
|
openIcon: openIcon,
|
|
closeIcon: closeIcon,
|
|
content: content,
|
|
isContentHide: true,
|
|
}
|
|
|
|
sideitem.widget.ConnectMousePressEvent(sideitem.toggleContent)
|
|
content.ConnectItemDoubleClicked(sideitem.fileDoubleClicked)
|
|
|
|
return sideitem
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) fileDoubleClicked(item *widgets.QListWidgetItem) {
|
|
filename := item.Text()
|
|
path := i.cwdpath
|
|
sep := ""
|
|
if runtime.GOOS == "windows" {
|
|
sep = `\`
|
|
} else {
|
|
sep = `/`
|
|
}
|
|
filepath := path + sep + filename
|
|
|
|
// exec := ""
|
|
// switch runtime.GOOS {
|
|
// case "darwin":
|
|
// exec = ":silent !open "
|
|
// case "windows":
|
|
// exec = ":silent !explorer "
|
|
// case "linux":
|
|
// exec = ":silent !xdg-open "
|
|
// }
|
|
exec := editor.config.Editor.FileOpenCmd + " "
|
|
|
|
execCommand := exec + filepath
|
|
for j, ws := range editor.workspaces {
|
|
if editor.side.items[j] == nil {
|
|
continue
|
|
}
|
|
sideItem := editor.side.items[j]
|
|
if i == sideItem {
|
|
go ws.nvim.Command(execCommand)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) toggleContent(event *gui.QMouseEvent) {
|
|
if i.hidden {
|
|
return
|
|
}
|
|
if i.isContentHide {
|
|
for j, ws := range editor.workspaces {
|
|
if editor.side.items[j] == nil {
|
|
continue
|
|
}
|
|
sideItem := editor.side.items[j]
|
|
if i == sideItem {
|
|
i.isContentHide = false
|
|
i.openContent()
|
|
go ws.nvim.Call("rpcnotify", nil, 0, "GonvimFiler", "redraw")
|
|
}
|
|
}
|
|
} else {
|
|
i.closeContent()
|
|
}
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) openContent() {
|
|
if i.content.StyleSheet() == "" {
|
|
i.content.SetStyleSheet(
|
|
fmt.Sprintf(`
|
|
QListWidget::item {
|
|
color: %s;
|
|
padding-left: 20px;
|
|
background-color: rgba(0, 0, 0, 0.0);
|
|
}
|
|
QListWidget::item:selected {
|
|
background-color: %s;
|
|
}`,
|
|
editor.colors.sideBarFg.String(),
|
|
editor.colors.selectedBg.String(),
|
|
),
|
|
)
|
|
}
|
|
i.openIcon.Show()
|
|
i.closeIcon.Hide()
|
|
i.isContentHide = false
|
|
i.content.Show()
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) closeContent() {
|
|
i.openIcon.Hide()
|
|
i.closeIcon.Show()
|
|
i.isContentHide = true
|
|
i.content.Hide()
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) setSideItemLabel(n int) {
|
|
if n == editor.active {
|
|
i.setActive()
|
|
} else {
|
|
i.setInactive()
|
|
}
|
|
i.label.SetContentsMargins(1, 3, 0, 3)
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) clear() {
|
|
i.content.Clear()
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) addItem(args []interface{}) {
|
|
filename := args[0].(string)
|
|
filetype := args[1].(string)
|
|
l := widgets.NewQListWidgetItem(i.content, 1)
|
|
var svg string
|
|
if filetype == `/` {
|
|
svg = editor.getSvg("directory", nil)
|
|
} else {
|
|
svg = editor.getSvg(filetype, nil)
|
|
}
|
|
pixmap := gui.NewQPixmap()
|
|
pixmap.LoadFromData2(core.NewQByteArray2(svg, len(svg)), "SVG", core.Qt__ColorOnly)
|
|
icon := gui.NewQIcon2(pixmap)
|
|
|
|
l.SetIcon(icon)
|
|
l.SetText(filename)
|
|
i.content.AddItem2(l)
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) resizeContent() {
|
|
rowNum := i.content.Count()
|
|
if rowNum > editor.config.FileExplore.MaxDisplayItems {
|
|
rowNum = editor.config.FileExplore.MaxDisplayItems
|
|
}
|
|
itemHeight := i.content.RectForIndex(i.content.IndexFromItem(i.content.Item(0))).Height()
|
|
i.content.SetFixedHeight(itemHeight * rowNum)
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) selectItem(args []interface{}) {
|
|
i.content.SetCurrentRow(util.ReflectToInt(args[0]))
|
|
}
|
|
|
|
func (side *WorkspaceSide) setColor() {
|
|
if side.fg.equals(editor.colors.fg) &&
|
|
side.sfg.equals(editor.colors.sideBarFg) &&
|
|
side.scrollFg.equals(editor.colors.scrollBarFg) &&
|
|
side.scrollBg.equals(editor.colors.scrollBarBg) &&
|
|
side.selectBg.equals(editor.colors.sideBarSelectedItemBg) &&
|
|
side.accent.equals(editor.colors.matchFg) {
|
|
|
|
return
|
|
}
|
|
|
|
side.fg = editor.colors.fg
|
|
side.sfg = editor.colors.sideBarFg
|
|
side.scrollFg = editor.colors.scrollBarFg
|
|
side.scrollBg = editor.colors.scrollBarBg
|
|
side.selectBg = editor.colors.sideBarSelectedItemBg
|
|
side.accent = editor.colors.matchFg
|
|
|
|
scrfg := side.scrollFg.String()
|
|
scrbg := side.scrollBg.StringTransparent()
|
|
hover := side.accent.String()
|
|
|
|
side.header.SetStyleSheet(fmt.Sprintf(" .QLabel{ color: %s;} ", side.sfg.String()))
|
|
side.widget.SetStyleSheet(
|
|
fmt.Sprintf(`
|
|
.QWidget { border: 0px solid #000; padding-top: 5px; background-color: rgba(0, 0, 0, 0); }
|
|
QWidget { color: %s; border-right: 0px solid; }
|
|
`, side.sfg.String()),
|
|
)
|
|
if side.scrollarea == nil {
|
|
return
|
|
}
|
|
side.scrollarea.SetStyleSheet(
|
|
fmt.Sprintf(`
|
|
.QScrollBar { border-width: 0px; background-color: %s; width: 5px; margin: 0 0 0 0; }
|
|
.QScrollBar::handle:vertical {background-color: %s; min-height: 25px;}
|
|
.QScrollBar::handle:vertical:hover {background-color: %s; min-height: 25px;}
|
|
.QScrollBar::add-line:vertical, .QScrollBar::sub-line:vertical { border: none; background: none; }
|
|
.QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; }`,
|
|
scrbg, scrfg, hover),
|
|
)
|
|
|
|
if len(editor.workspaces) == 1 {
|
|
side.items[0].active = true
|
|
// side.items[0].labelWidget.SetStyleSheet(
|
|
// fmt.Sprintf(
|
|
// " * { background-color: %s; color: %s; }",
|
|
// side.selectBg.String(), side.sfg.String(),
|
|
// ),
|
|
// )
|
|
transparent := transparent() * transparent()
|
|
side.items[0].labelWidget.SetStyleSheet(
|
|
fmt.Sprintf(
|
|
" * { background-color: rgba(%d, %d, %d, %f); color: %s; }",
|
|
side.selectBg.R, side.selectBg.G, side.selectBg.B,
|
|
transparent,
|
|
side.fg.String(),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
func (side *WorkspaceSide) setColorForItems() {
|
|
for _, item := range side.items {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
if item.hidden {
|
|
continue
|
|
}
|
|
item.content.SetStyleSheet(
|
|
fmt.Sprintf(`
|
|
QListWidget::item {
|
|
color: %s;
|
|
padding-left: 20px;
|
|
background-color: rgba(0, 0, 0, 0.0);
|
|
}
|
|
QListWidget::item:selected {
|
|
background-color: %s;
|
|
}`,
|
|
editor.colors.sideBarFg.String(),
|
|
editor.colors.selectedBg.String(),
|
|
),
|
|
)
|
|
item.hide()
|
|
item.show()
|
|
// update icon color
|
|
for i := 0; i < item.content.Count(); i++ {
|
|
l := item.content.Item(i)
|
|
if l == nil {
|
|
break
|
|
}
|
|
filename := l.Text()
|
|
parts := strings.SplitN(filename, ".", -1)
|
|
filetype := ""
|
|
if len(parts) > 1 {
|
|
filetype = parts[len(parts)-1]
|
|
}
|
|
// If it is directory
|
|
if filename[len(filename)-1] == '/' {
|
|
filetype = string("/")
|
|
}
|
|
var svg string
|
|
if filetype == `/` {
|
|
svg = editor.getSvg("directory", nil)
|
|
} else {
|
|
svg = editor.getSvg(filetype, nil)
|
|
}
|
|
pixmap := gui.NewQPixmap()
|
|
pixmap.LoadFromData2(core.NewQByteArray2(svg, len(svg)), "SVG", core.Qt__ColorOnly)
|
|
icon := gui.NewQIcon2(pixmap)
|
|
|
|
l.SetIcon(icon)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) setActive() {
|
|
if editor.colors.fg == nil {
|
|
return
|
|
}
|
|
if editor.side.scrollarea == nil {
|
|
return
|
|
}
|
|
i.active = true
|
|
bg := editor.colors.sideBarSelectedItemBg
|
|
fg := editor.colors.fg
|
|
transparent := transparent() * transparent()
|
|
i.labelWidget.SetStyleSheet(
|
|
fmt.Sprintf(
|
|
" * { background-color: rgba(%d, %d, %d, %f); color: %s; }",
|
|
bg.R, bg.G, bg.B,
|
|
transparent,
|
|
fg.String(),
|
|
),
|
|
)
|
|
svgOpenContent := editor.getSvg("chevron-down", fg)
|
|
i.openIcon.Load2(core.NewQByteArray2(svgOpenContent, len(svgOpenContent)))
|
|
svgCloseContent := editor.getSvg("chevron-right", fg)
|
|
i.closeIcon.Load2(core.NewQByteArray2(svgCloseContent, len(svgCloseContent)))
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) setInactive() {
|
|
if editor.colors.fg == nil {
|
|
return
|
|
}
|
|
if editor.side.scrollarea == nil {
|
|
return
|
|
}
|
|
i.active = false
|
|
fg := editor.colors.inactiveFg
|
|
i.labelWidget.SetStyleSheet(
|
|
fmt.Sprintf(
|
|
" * { background-color: rgba(0, 0, 0, 0); color: %s; }",
|
|
fg.String(),
|
|
),
|
|
)
|
|
svgOpenContent := editor.getSvg("chevron-down", fg)
|
|
i.openIcon.Load2(core.NewQByteArray2(svgOpenContent, len(svgOpenContent)))
|
|
svgCloseContent := editor.getSvg("chevron-right", fg)
|
|
i.closeIcon.Load2(core.NewQByteArray2(svgCloseContent, len(svgCloseContent)))
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) show() {
|
|
if !i.hidden {
|
|
return
|
|
}
|
|
i.hidden = false
|
|
i.label.Show()
|
|
|
|
if !i.isContentHide {
|
|
i.content.Show()
|
|
i.openIcon.Show()
|
|
i.closeIcon.Hide()
|
|
} else {
|
|
i.content.Hide()
|
|
i.openIcon.Hide()
|
|
i.closeIcon.Show()
|
|
}
|
|
}
|
|
|
|
func (i *WorkspaceSideItem) hide() {
|
|
if i.hidden {
|
|
return
|
|
}
|
|
i.hidden = true
|
|
i.label.Hide()
|
|
i.openIcon.Hide()
|
|
i.closeIcon.Hide()
|
|
|
|
i.content.Hide()
|
|
}
|