mirror of
https://github.com/akiyosi/goneovim.git
synced 2025-08-05 10:34:36 +02:00
1895 lines
42 KiB
Go
1895 lines
42 KiB
Go
package editor
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/akiyosi/goneovim/util"
|
|
"github.com/akiyosi/qt/core"
|
|
"github.com/akiyosi/qt/gui"
|
|
"github.com/akiyosi/qt/widgets"
|
|
"github.com/bluele/gcache"
|
|
"github.com/neovim/go-client/nvim"
|
|
)
|
|
|
|
var globalOrder int
|
|
|
|
// Screen is the main editor area
|
|
type Screen struct {
|
|
cache Cache
|
|
bgcache Cache
|
|
tooltip *IMETooltip
|
|
font *Font
|
|
fallbackfonts []*Font
|
|
fontwide *Font
|
|
fallbackfontwides []*Font
|
|
hlAttrDef map[int]*Highlight
|
|
widget *widgets.QWidget
|
|
ws *Workspace
|
|
highlightGroup map[string]int
|
|
windows sync.Map
|
|
name string
|
|
cursor [2]int
|
|
height int
|
|
width int
|
|
resizeCount uint
|
|
topLevelGrid int
|
|
lastGridLineGrid int
|
|
}
|
|
|
|
type Cache struct {
|
|
gcache.Cache
|
|
}
|
|
|
|
func newScreen() *Screen {
|
|
widget := widgets.NewQWidget(nil, 0)
|
|
widget.SetContentsMargins(0, 0, 0, 0)
|
|
widget.SetStyleSheet(" * { background-color: rgba(0, 0, 0, 0);}")
|
|
|
|
screen := &Screen{
|
|
widget: widget,
|
|
windows: sync.Map{},
|
|
cursor: [2]int{0, 0},
|
|
highlightGroup: make(map[string]int),
|
|
cache: newCache(),
|
|
bgcache: newBrushCache(),
|
|
}
|
|
|
|
widget.ConnectMousePressEvent(screen.mousePressEvent)
|
|
widget.ConnectMouseReleaseEvent(screen.mouseEvent)
|
|
widget.ConnectMouseMoveEvent(screen.mouseEvent)
|
|
|
|
return screen
|
|
}
|
|
|
|
func (s *Screen) initInputMethodWidget() {
|
|
tooltip := NewIMETooltip(s.widget, 0)
|
|
tooltip.SetVisible(false)
|
|
tooltip.SetAttribute(core.Qt__WA_OpaquePaintEvent, true)
|
|
tooltip.ConnectPaintEvent(tooltip.paint)
|
|
tooltip.s = s
|
|
s.tooltip = tooltip
|
|
}
|
|
|
|
func fileOpenInBuf(file string) {
|
|
isModified, _ := editor.workspaces[editor.active].nvim.CommandOutput("echo &modified")
|
|
if isModified == "1" {
|
|
editor.workspaces[editor.active].nvim.Command(fmt.Sprintf(":tabnew %s", file))
|
|
} else {
|
|
editor.workspaces[editor.active].nvim.Command(fmt.Sprintf(":e %s", file))
|
|
}
|
|
}
|
|
|
|
func (s *Screen) howToOpen(file string) {
|
|
message := "[Goneovim] Do you want to diff between the file being dropped and the current buffer?"
|
|
opts := []*NotifyButton{}
|
|
opt1 := &NotifyButton{
|
|
action: func() {
|
|
editor.workspaces[editor.active].nvim.Command(fmt.Sprintf(":vertical diffsplit %s", file))
|
|
},
|
|
text: "Yes",
|
|
}
|
|
opts = append(opts, opt1)
|
|
|
|
opt2 := &NotifyButton{
|
|
action: func() {
|
|
fileOpenInBuf(file)
|
|
},
|
|
text: "No, I want to open with a new buffer",
|
|
}
|
|
opts = append(opts, opt2)
|
|
|
|
editor.pushNotification(NotifyInfo, 0, message, notifyOptionArg(opts))
|
|
}
|
|
|
|
func (s *Screen) waitTime() time.Duration {
|
|
var ret time.Duration
|
|
switch s.resizeCount {
|
|
case 0:
|
|
ret = 10
|
|
case 1:
|
|
ret = 100
|
|
default:
|
|
ret = 1000
|
|
}
|
|
|
|
s.resizeCount++
|
|
return ret
|
|
}
|
|
|
|
func (s *Screen) updateSize() {
|
|
s.ws.fontMutex.Lock()
|
|
defer s.ws.fontMutex.Unlock()
|
|
|
|
ws := s.ws
|
|
|
|
newCols := int(float64(s.width) / s.font.cellwidth)
|
|
newRows := s.height / s.font.lineHeight
|
|
|
|
// Adjust the position of the message grid.
|
|
s.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])
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
isNeedTryResize := (newCols != ws.cols || newRows != ws.rows)
|
|
if !isNeedTryResize {
|
|
return
|
|
}
|
|
|
|
ws.cols = newCols
|
|
ws.rows = newRows
|
|
|
|
if !ws.uiAttached {
|
|
return
|
|
}
|
|
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.font != nil {
|
|
win.width = 0
|
|
win.height = 0
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
s.uiTryResize(newCols, newRows)
|
|
|
|
}
|
|
|
|
func (s *Screen) uiTryResize(cols, rows int) {
|
|
if cols <= 0 || rows <= 0 {
|
|
return
|
|
}
|
|
|
|
ws := s.ws
|
|
done := make(chan error, 5)
|
|
var result error
|
|
go func() {
|
|
result = ws.nvim.TryResizeUI(cols, rows)
|
|
done <- result
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(s.waitTime() * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func (s *Screen) getGrid(wid nvim.Window) (grid *Window, ok bool) {
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
|
|
win.updateMutex.RLock()
|
|
id := win.id
|
|
win.updateMutex.RUnlock()
|
|
|
|
if id == wid {
|
|
grid = win
|
|
ok = true
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return grid, ok
|
|
}
|
|
|
|
func (s *Screen) getWindow(grid int) (*Window, bool) {
|
|
winITF, ok := s.windows.Load(grid)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return nil, false
|
|
}
|
|
|
|
return win, true
|
|
}
|
|
|
|
func (s *Screen) storeWindow(grid int, win *Window) {
|
|
s.windows.Store(grid, win)
|
|
}
|
|
|
|
func (s *Screen) deleteWindow(grid int) {
|
|
winITF, loaded := s.windows.LoadAndDelete(grid)
|
|
if loaded {
|
|
win := winITF.(*Window)
|
|
if win != nil {
|
|
win.DestroyWindow()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Screen) lenWindows() int {
|
|
length := 0
|
|
s.windows.Range(func(_, _ interface{}) bool {
|
|
length++
|
|
return true
|
|
})
|
|
|
|
return length
|
|
}
|
|
|
|
func (s *Screen) gridFont(update interface{}) {
|
|
|
|
// Get current window
|
|
grid := s.ws.cursor.gridid
|
|
if !editor.config.Editor.ExtCmdline {
|
|
grid = s.ws.cursor.bufferGridid
|
|
}
|
|
|
|
win, ok := s.getWindow(grid)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
updateStr, ok := update.(string)
|
|
if !ok {
|
|
return
|
|
}
|
|
if updateStr == "" {
|
|
return
|
|
}
|
|
|
|
parts := strings.Split(updateStr, ":")
|
|
if len(parts) < 1 {
|
|
return
|
|
}
|
|
|
|
// fontfamily := parts[0]
|
|
// height := 14
|
|
|
|
// for _, p := range parts[1:] {
|
|
// if strings.HasPrefix(p, "h") {
|
|
// var err error
|
|
// height, err = strconv.Atoi(p[1:])
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// } else if strings.HasPrefix(p, "w") {
|
|
// var err error
|
|
// width, err := strconv.Atoi(p[1:])
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// height = 2 * width
|
|
// }
|
|
// }
|
|
|
|
oldWidth := float64(win.cols) * win.getFont().cellwidth
|
|
oldHeight := win.rows * win.getFont().lineHeight
|
|
win.width = oldWidth
|
|
win.height = oldHeight
|
|
win.localWindows = &[4]localWindow{}
|
|
|
|
win.fallbackfonts = nil
|
|
s.ws.parseAndApplyFont(updateStr, &win.font, &win.fallbackfonts)
|
|
|
|
if win.font == nil {
|
|
return
|
|
}
|
|
|
|
// Calculate new cols, rows of current grid
|
|
newCols := int(oldWidth / win.font.cellwidth)
|
|
newRows := oldHeight / win.font.lineHeight
|
|
|
|
// Cache
|
|
cache := win.cache
|
|
if cache == (Cache{}) {
|
|
cache := newCache()
|
|
win.cache = cache
|
|
} else {
|
|
win.paintMutex.Lock()
|
|
win.cache.purge()
|
|
win.paintMutex.Unlock()
|
|
}
|
|
|
|
_ = s.ws.nvim.TryResizeUIGrid(win.grid, newCols, newRows)
|
|
|
|
s.ws.cursor.font = win.getFont()
|
|
s.ws.cursor.fallbackfonts = win.fallbackfonts
|
|
|
|
if win.isExternal {
|
|
width := int(float64(newCols)*win.font.cellwidth) + EXTWINBORDERSIZE*2
|
|
height := newRows*win.font.lineHeight + EXTWINBORDERSIZE*2
|
|
win.extwin.Resize2(width, height)
|
|
}
|
|
}
|
|
|
|
func (s *Screen) purgeTextCacheForWins() {
|
|
if !editor.config.Editor.CachedDrawing {
|
|
return
|
|
}
|
|
s.cache.purge()
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.font == nil {
|
|
return true
|
|
}
|
|
cache := win.cache
|
|
if cache == (Cache{}) {
|
|
return true
|
|
}
|
|
win.paintMutex.Lock()
|
|
win.cache.purge()
|
|
win.paintMutex.Unlock()
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *Screen) bottomWindowPos() int {
|
|
pos := 0
|
|
s.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.isFloatWin {
|
|
return true
|
|
}
|
|
if !win.IsVisible() {
|
|
return true
|
|
}
|
|
|
|
position := win.pos[1]*win.s.font.lineHeight + win.Rect().Bottom()
|
|
if pos < position {
|
|
pos = position
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return pos
|
|
}
|
|
|
|
func (s *Screen) mousePressEvent(event *gui.QMouseEvent) {
|
|
if !s.ws.isMouseEnabled {
|
|
return
|
|
}
|
|
|
|
s.mouseEvent(event)
|
|
if !editor.config.Editor.ClickEffect {
|
|
return
|
|
}
|
|
|
|
// TODO: If a float window exists, the process of selecting it is necessary.
|
|
|
|
win, ok := s.getWindow(s.ws.cursor.gridid)
|
|
if !ok {
|
|
return
|
|
}
|
|
font := win.getFont()
|
|
|
|
widget := widgets.NewQWidget(nil, 0)
|
|
widget.SetStyleSheet(" * { background-color: rgba(0, 0, 0, 0);}")
|
|
widget.SetParent(win)
|
|
widget.SetFixedSize2(font.lineHeight*4/3, font.lineHeight*4/3)
|
|
widget.Show()
|
|
widget.ConnectPaintEvent(func(e *gui.QPaintEvent) {
|
|
p := gui.NewQPainter2(widget)
|
|
p.SetRenderHint(gui.QPainter__Antialiasing, true)
|
|
p.SetRenderHint(gui.QPainter__HighQualityAntialiasing, true)
|
|
rgbAccent := hexToRGBA(editor.config.SideBar.AccentColor)
|
|
|
|
x := float64(font.lineHeight * 2 / 3)
|
|
y := float64(font.lineHeight * 2 / 3)
|
|
r := float64(font.lineHeight * 2 / 3)
|
|
point := core.NewQPointF3(x, y)
|
|
|
|
rgbAccent = newRGBA(rgbAccent.R, rgbAccent.G, rgbAccent.B, 0.1)
|
|
p.SetBrush(gui.NewQBrush3(rgbAccent.QColor(), core.Qt__SolidPattern))
|
|
p.DrawEllipse4(
|
|
point,
|
|
r,
|
|
r,
|
|
)
|
|
|
|
rgbAccent = newRGBA(rgbAccent.R, rgbAccent.G, rgbAccent.B, 0.2)
|
|
p.SetBrush(gui.NewQBrush3(rgbAccent.QColor(), core.Qt__SolidPattern))
|
|
p.DrawEllipse4(
|
|
point,
|
|
r*0.9,
|
|
r*0.9,
|
|
)
|
|
rgbAccent = newRGBA(rgbAccent.R, rgbAccent.G, rgbAccent.B, 0.3)
|
|
p.SetBrush(gui.NewQBrush3(rgbAccent.QColor(), core.Qt__SolidPattern))
|
|
p.DrawEllipse4(
|
|
point,
|
|
r*0.85,
|
|
r*0.85,
|
|
)
|
|
rgbAccent = newRGBA(rgbAccent.R, rgbAccent.G, rgbAccent.B, 0.4)
|
|
p.SetBrush(gui.NewQBrush3(rgbAccent.QColor(), core.Qt__SolidPattern))
|
|
p.DrawEllipse4(
|
|
point,
|
|
r*0.8,
|
|
r*0.8,
|
|
)
|
|
rgbAccent = newRGBA(rgbAccent.R, rgbAccent.G, rgbAccent.B, 0.7)
|
|
p.SetBrush(gui.NewQBrush3(rgbAccent.QColor(), core.Qt__SolidPattern))
|
|
p.DrawEllipse4(
|
|
point,
|
|
r*0.7,
|
|
r*0.7,
|
|
)
|
|
rgbAccent = newRGBA(rgbAccent.R, rgbAccent.G, rgbAccent.B, 0.9)
|
|
p.SetBrush(gui.NewQBrush3(rgbAccent.QColor(), core.Qt__SolidPattern))
|
|
p.DrawEllipse4(
|
|
point,
|
|
r*0.65,
|
|
r*0.65,
|
|
)
|
|
|
|
p.DestroyQPainter()
|
|
})
|
|
widget.Move2(
|
|
event.Pos().X()-font.lineHeight*2/3-1,
|
|
event.Pos().Y()-font.lineHeight*2/3-1,
|
|
)
|
|
|
|
eff := widgets.NewQGraphicsOpacityEffect(widget)
|
|
widget.SetGraphicsEffect(eff)
|
|
a := core.NewQPropertyAnimation2(eff, core.NewQByteArray2("opacity", len("opacity")), widget)
|
|
a.SetDuration(500)
|
|
a.SetStartValue(core.NewQVariant5(1))
|
|
a.SetEndValue(core.NewQVariant5(0))
|
|
a.SetEasingCurve(core.NewQEasingCurve(core.QEasingCurve__InOutQuart))
|
|
a.Start(core.QAbstractAnimation__DeletionPolicy(core.QAbstractAnimation__DeleteWhenStopped))
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
widget.Hide()
|
|
s.update()
|
|
}()
|
|
}
|
|
|
|
func (s *Screen) mouseEvent(event *gui.QMouseEvent) {
|
|
if !s.ws.isMouseEnabled {
|
|
return
|
|
}
|
|
|
|
var targetwin *Window
|
|
|
|
// If a mouse event has already occurred and the mouse is being
|
|
// used to drag content, the window being operated on will be the target window.
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if !win.IsVisible() {
|
|
return true
|
|
}
|
|
if win.lastMouseEvent != nil && win.lastMouseEvent.event != nil {
|
|
if win.lastMouseEvent.event.Type() == core.QEvent__MouseMove {
|
|
targetwin = win
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
if targetwin == nil {
|
|
|
|
// If there is an externalized float window and
|
|
// the mouse pointer is in that window,
|
|
// make that window the target window.
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if !win.IsVisible() {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
return true
|
|
}
|
|
if win.isExternal {
|
|
if win.grid == s.ws.cursor.gridid {
|
|
targetwin = win
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
// If a float window exists, the float window with the smallest size
|
|
// that covers the position of the mouse event that occurred
|
|
// is used as the target window.
|
|
if targetwin == nil {
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if !win.IsVisible() {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
return true
|
|
}
|
|
if win.isFloatWin && !win.isExternal {
|
|
if win.Geometry().Contains(event.Pos(), true) {
|
|
if targetwin != nil {
|
|
if targetwin.Geometry().Contains2(win.Geometry(), true) {
|
|
targetwin = win
|
|
}
|
|
} else {
|
|
if win.Geometry().Contains(event.Pos(), true) {
|
|
targetwin = win
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
// If none of the above processes apply,
|
|
// the target window is a normal window that covers the position
|
|
// where the mouse event occurs.
|
|
if targetwin == nil {
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if !win.IsVisible() {
|
|
return true
|
|
}
|
|
if win.isFloatWin || win.isExternal {
|
|
return true
|
|
}
|
|
|
|
// Judgment of the global grid is performed in
|
|
// a separate process when there is no corresponding result
|
|
// in the judgment of the coverage of mouse click coordinates
|
|
// in all grids.
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
|
|
if win.Geometry().Contains(event.Pos(), true) {
|
|
targetwin = win
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
if targetwin == nil {
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
if win.Geometry().Contains(event.Pos(), true) {
|
|
targetwin = win
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
if targetwin == nil {
|
|
return
|
|
}
|
|
|
|
// Reset the mouse event state of each window.
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.lastMouseEvent != nil {
|
|
win.lastMouseEvent.event = nil
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
var localpos *core.QPointF
|
|
if targetwin.isExternal {
|
|
localpos = core.NewQPointF3(
|
|
event.ScreenPos().X()-float64(targetwin.extwin.Pos().X()+targetwin.Pos().X()),
|
|
event.ScreenPos().Y()-float64(targetwin.extwin.Pos().Y()+targetwin.Pos().Y()),
|
|
)
|
|
} else {
|
|
// Fixed mouse events handling in relation to #316#issuecomment-1039978355 fixes.
|
|
offsetX := float64(targetwin.Pos().X())
|
|
offsetY := float64(targetwin.Pos().Y())
|
|
|
|
localpos = core.NewQPointF3(
|
|
event.LocalPos().X()-offsetX,
|
|
event.LocalPos().Y()-offsetY,
|
|
)
|
|
}
|
|
|
|
targetwin.mouseEvent(
|
|
gui.NewQMouseEvent3(
|
|
event.Type(),
|
|
localpos,
|
|
event.WindowPos(),
|
|
event.ScreenPos(),
|
|
event.Button(),
|
|
event.Buttons(),
|
|
event.Modifiers(),
|
|
),
|
|
)
|
|
}
|
|
|
|
func (s *Screen) gridResize(args []interface{}) {
|
|
var gridid gridId
|
|
var rows, cols int
|
|
for _, arg := range args {
|
|
gridid = util.ReflectToInt(arg.([]interface{})[0])
|
|
cols = util.ReflectToInt(arg.([]interface{})[1])
|
|
rows = util.ReflectToInt(arg.([]interface{})[2])
|
|
|
|
// for debug
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
// resize neovim's window
|
|
s.resizeWindow(gridid, cols, rows)
|
|
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if win.isFloatWin && !win.isMsgGrid {
|
|
win.setFloatWindowPosition()
|
|
}
|
|
win.move(win.pos[0], win.pos[1], win.anchorwin)
|
|
win.show()
|
|
|
|
// If events related to the global grid are included
|
|
// Determine to resize the application window
|
|
if s.name != "minimap" && s.ws.uiAttached {
|
|
if win.grid == 1 {
|
|
if !editor.isBindNvimSizeToAppwin {
|
|
continue
|
|
}
|
|
if !(s.ws.cols == cols && s.ws.rows == rows) {
|
|
s.ws.cols = cols
|
|
s.ws.rows = rows
|
|
s.ws.updateApplicationWindowSize(cols, rows)
|
|
}
|
|
}
|
|
if win.grid != 1 && !win.isMsgGrid {
|
|
if !editor.isBindNvimSizeToAppwin {
|
|
editor.isBindNvimSizeToAppwin = true
|
|
newCols := s.ws.cols
|
|
if editor.isSetColumns {
|
|
newCols = editor.initialColumns
|
|
}
|
|
newRows := s.ws.rows
|
|
if editor.isSetLines {
|
|
newRows = editor.initialLines
|
|
}
|
|
s.uiTryResize(newCols, newRows)
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Screen) resizeWindow(gridid gridId, cols int, rows int) {
|
|
win, _ := s.getWindow(gridid)
|
|
|
|
if win != nil {
|
|
if win.cols == cols && win.rows == rows {
|
|
return
|
|
}
|
|
}
|
|
|
|
if win != nil && win.snapshot != nil {
|
|
win.dropScreenSnapshot()
|
|
}
|
|
|
|
// make new size content
|
|
content := make([][]*Cell, rows)
|
|
contentMask := make([][]bool, rows)
|
|
contentMaskOld := make([][]bool, rows)
|
|
lenLine := make([]int, rows)
|
|
lenContent := make([]int, rows)
|
|
lenOldContent := make([]int, rows)
|
|
|
|
for i := 0; i < rows; i++ {
|
|
content[i] = make([]*Cell, cols)
|
|
contentMask[i] = make([]bool, cols)
|
|
contentMaskOld[i] = make([]bool, cols)
|
|
lenContent[i] = cols - 1
|
|
}
|
|
|
|
for i := 0; i < rows; i++ {
|
|
// if win != nil {
|
|
// if i < len(win.content) {
|
|
// // lenLine[i] = win.lenLine[i]
|
|
// lenContent[i] = win.lenContent[i]
|
|
// if i < len(win.lenOldContent) {
|
|
// lenOldContent[i] = win.lenOldContent[i]
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
for j := 0; j < cols; j++ {
|
|
|
|
if win != nil {
|
|
if i < len(win.content) && j < len(win.content[i]) {
|
|
if i < len(win.contentMaskOld) && j < len(win.contentMaskOld[i]) {
|
|
contentMaskOld[i][j] = win.contentMaskOld[i][j]
|
|
}
|
|
contentMask[i][j] = win.contentMask[i][j]
|
|
content[i][j] = win.content[i][j]
|
|
}
|
|
}
|
|
if content[i][j] == nil {
|
|
content[i][j] = &Cell{
|
|
highlight: s.hlAttrDef[0],
|
|
char: " ",
|
|
normalWidth: false,
|
|
covered: false,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if win == nil {
|
|
win = s.newWindowGird(gridid)
|
|
}
|
|
|
|
win.redrawMutex.Lock()
|
|
|
|
// winOldCols := win.cols
|
|
// winOldRows := win.rows
|
|
|
|
win.lenLine = lenLine
|
|
win.lenContent = lenContent
|
|
win.lenOldContent = lenOldContent
|
|
win.content = content
|
|
win.contentMask = contentMask
|
|
win.contentMaskOld = contentMaskOld
|
|
win.cols = cols
|
|
win.rows = rows
|
|
win.redrawMutex.Unlock()
|
|
|
|
// s.resizeIndependentFontGrid(win, winOldCols, winOldRows)
|
|
|
|
font := win.getFont()
|
|
|
|
width := int(math.Ceil(float64(cols) * font.cellwidth))
|
|
height := rows * font.lineHeight
|
|
|
|
win.setGridGeometry(width, height)
|
|
|
|
// win.move(win.pos[0], win.pos[1])
|
|
// win.show()
|
|
|
|
win.queueRedrawAll()
|
|
}
|
|
|
|
func (s *Screen) newWindowGird(gridid int) (win *Window) {
|
|
win = newWindow()
|
|
|
|
win.s = s
|
|
s.storeWindow(gridid, win)
|
|
|
|
win.SetParent(s.widget)
|
|
|
|
win.grid = gridid
|
|
|
|
// set scroll
|
|
if s.name != "minimap" {
|
|
win.ConnectWheelEvent(win.wheelEvent)
|
|
}
|
|
|
|
s.ws.cursor.raise()
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Screen) resizeIndependentFontGrid(win *Window, oldCols, oldRows int) {
|
|
var isExistMsgGrid bool
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
isExistMsgGrid = true
|
|
}
|
|
return true
|
|
})
|
|
|
|
if !isExistMsgGrid && s.lenWindows() < 3 {
|
|
return
|
|
}
|
|
if isExistMsgGrid && s.lenWindows() < 4 {
|
|
return
|
|
}
|
|
if oldCols == 0 && oldRows == 0 {
|
|
return
|
|
}
|
|
// var width, height int
|
|
deltaCols := win.cols - oldCols
|
|
deltaRows := win.rows - oldRows
|
|
absDeltaCols := math.Abs(float64(deltaCols))
|
|
absDeltaRows := math.Abs(float64(deltaRows))
|
|
if absDeltaCols > 0 && absDeltaRows > 0 {
|
|
return
|
|
}
|
|
var isResizeWidth, isResizeHeight bool
|
|
if absDeltaCols > 0 {
|
|
isResizeWidth = true
|
|
} else if absDeltaRows > 0 {
|
|
isResizeHeight = true
|
|
}
|
|
|
|
leftWindowPos := win.pos[0] + oldCols + 1 + deltaCols
|
|
topWindowPos := win.pos[1] + oldRows + 1 + deltaRows
|
|
|
|
s.windows.Range(func(_, winITF interface{}) bool {
|
|
w := winITF.(*Window)
|
|
if w == nil {
|
|
return true
|
|
}
|
|
if w.grid == 1 {
|
|
return true
|
|
}
|
|
if w.isMsgGrid {
|
|
return true
|
|
}
|
|
if w.grid == win.grid {
|
|
return true
|
|
}
|
|
if w.font == nil {
|
|
return true
|
|
}
|
|
|
|
if isResizeWidth && w.width > 0 {
|
|
// right window is gridfont window
|
|
if w.localWindows[2].grid == win.grid || (w.pos[1] == win.pos[1] && w.pos[0] == leftWindowPos) {
|
|
if w.localWindows[2].grid == 0 {
|
|
w.localWindows[2].grid = win.grid
|
|
}
|
|
if !w.localWindows[2].isResized {
|
|
w.localWindows[2].isResized = true
|
|
w.localWindows[2].localWidth = w.width + float64(oldCols)*win.getFont().cellwidth
|
|
}
|
|
newWidth := w.localWindows[2].localWidth - (float64(win.cols) * win.getFont().cellwidth)
|
|
newCols := int(newWidth / w.font.cellwidth)
|
|
if newCols != w.cols {
|
|
_ = s.ws.nvim.TryResizeUIGrid(w.grid, newCols, w.rows)
|
|
w.width = float64(newCols) * w.getFont().cellwidth
|
|
w.localWindows[0].isResized = false
|
|
}
|
|
}
|
|
|
|
// left window is gridfont window
|
|
// calculate win window posision aa w window coordinate
|
|
var resizeflag bool
|
|
winPosX := float64(win.pos[0]) * win.s.font.cellwidth
|
|
rightWindowPos1 := float64(w.cols)*w.getFont().cellwidth + float64(w.pos[0]+1-deltaCols+1)*win.s.font.cellwidth
|
|
rightWindowPos2 := float64(w.cols-1)*w.getFont().cellwidth + float64(w.pos[0]+1-deltaCols+1)*win.s.font.cellwidth
|
|
rightWindowPos := int(float64(w.cols)*w.getFont().cellwidth/win.s.font.cellwidth) + w.pos[0] + 1 - deltaCols + 1
|
|
if win.s.font.cellwidth < w.getFont().cellwidth {
|
|
resizeflag = winPosX <= rightWindowPos1 && winPosX >= rightWindowPos2
|
|
} else {
|
|
resizeflag = win.pos[0] == rightWindowPos
|
|
}
|
|
if w.localWindows[0].grid == win.grid || (w.pos[1] == win.pos[1] && resizeflag) {
|
|
if w.localWindows[0].grid == 0 {
|
|
w.localWindows[0].grid = win.grid
|
|
}
|
|
if !w.localWindows[0].isResized {
|
|
w.localWindows[0].isResized = true
|
|
w.localWindows[0].localWidth = w.width + float64(oldCols)*win.getFont().cellwidth
|
|
}
|
|
newWidth := w.localWindows[0].localWidth - (float64(win.cols) * win.getFont().cellwidth)
|
|
newCols := int(newWidth / w.font.cellwidth)
|
|
if newCols != w.cols {
|
|
_ = s.ws.nvim.TryResizeUIGrid(w.grid, newCols, w.rows)
|
|
w.width = float64(newCols) * w.getFont().cellwidth
|
|
w.localWindows[2].isResized = false
|
|
}
|
|
}
|
|
|
|
}
|
|
if isResizeHeight && w.height > 0 {
|
|
// bottom window is gridfont window
|
|
if w.localWindows[1].grid == win.grid || (w.pos[0] == win.pos[0] && w.pos[1] == topWindowPos) {
|
|
if w.localWindows[1].grid == 0 {
|
|
w.localWindows[1].grid = win.grid
|
|
}
|
|
if !w.localWindows[1].isResized {
|
|
w.localWindows[1].isResized = true
|
|
w.localWindows[1].localHeight = w.height + oldRows*win.getFont().lineHeight
|
|
}
|
|
newHeight := w.localWindows[1].localHeight - (win.rows * win.getFont().lineHeight)
|
|
newRows := newHeight / w.font.lineHeight
|
|
if newRows != w.rows {
|
|
_ = s.ws.nvim.TryResizeUIGrid(w.grid, w.cols, newRows)
|
|
w.height = newRows * w.getFont().lineHeight
|
|
w.localWindows[3].isResized = false
|
|
}
|
|
}
|
|
|
|
// top window is gridfont window
|
|
// calculate win window posision aa w window coordinate
|
|
var resizeflag bool
|
|
winPosY := win.pos[1] * win.s.font.lineHeight
|
|
bottomWindowPos1 := w.rows*w.getFont().lineHeight + (w.pos[1]+1-deltaRows+1)*win.s.font.lineHeight
|
|
bottomWindowPos2 := (w.rows-1)*w.getFont().lineHeight + (w.pos[1]+1-deltaRows+1)*win.s.font.lineHeight
|
|
bottomWindowPos := (w.rows * w.getFont().lineHeight / win.s.font.lineHeight) + w.pos[1] + 1 - deltaRows + 1
|
|
if win.s.font.lineHeight < w.getFont().lineHeight {
|
|
resizeflag = winPosY <= bottomWindowPos1 && winPosY >= bottomWindowPos2
|
|
} else {
|
|
resizeflag = win.pos[1] == bottomWindowPos
|
|
}
|
|
if w.localWindows[3].grid == win.grid || (w.pos[0] == win.pos[0] && resizeflag) {
|
|
if w.localWindows[3].grid == 0 {
|
|
w.localWindows[3].grid = win.grid
|
|
}
|
|
if !w.localWindows[3].isResized {
|
|
w.localWindows[3].isResized = true
|
|
w.localWindows[3].localHeight = w.height + oldRows*win.getFont().lineHeight
|
|
}
|
|
newHeight := w.localWindows[3].localHeight - (win.rows * win.getFont().lineHeight)
|
|
newRows := newHeight / w.font.lineHeight
|
|
if newRows != w.rows {
|
|
_ = s.ws.nvim.TryResizeUIGrid(w.grid, w.cols, newRows)
|
|
w.height = newRows * w.getFont().lineHeight
|
|
w.localWindows[1].isResized = false
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *Screen) gridCursorGoto(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
|
|
x := util.ReflectToInt(arg.([]interface{})[1])
|
|
y := util.ReflectToInt(arg.([]interface{})[2])
|
|
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// Suppress unnecessary detours of the smooth cursor.
|
|
if win.isMsgGrid && x == 0 && y == 0 {
|
|
continue
|
|
}
|
|
if win.isMsgGrid && editor.config.Editor.ExtCmdline {
|
|
continue
|
|
}
|
|
|
|
s.cursor[0] = x
|
|
s.cursor[1] = y
|
|
|
|
if !win.isMsgGrid {
|
|
s.ws.cursor.prevGridid = s.ws.cursor.bufferGridid
|
|
}
|
|
if s.ws.cursor.gridid != win.grid {
|
|
if !win.isMsgGrid {
|
|
s.ws.cursor.bufferGridid = gridid
|
|
}
|
|
|
|
// Set new cursor grid id
|
|
s.ws.cursor.gridid = gridid
|
|
if s.ws.cursor.prevGridid == 0 {
|
|
s.ws.cursor.prevGridid = gridid
|
|
}
|
|
|
|
win.raise()
|
|
win.s.tooltip.setGrid()
|
|
|
|
// reset smooth scroll scrolling offset
|
|
win.scrollPixels2 = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Screen) setTopLevelGrid(n int) {
|
|
s.topLevelGrid = n
|
|
}
|
|
|
|
func (s *Screen) setHlAttrDef(args []interface{}) {
|
|
var h map[int]*Highlight
|
|
if s.hlAttrDef == nil {
|
|
h = make(map[int]*Highlight)
|
|
} else {
|
|
h = s.hlAttrDef
|
|
}
|
|
|
|
h[0] = &Highlight{
|
|
foreground: s.ws.foreground,
|
|
background: s.ws.background,
|
|
}
|
|
|
|
for _, arg := range args {
|
|
id := util.ReflectToInt(arg.([]interface{})[0])
|
|
h[id] = s.makeHighlight(arg)
|
|
}
|
|
|
|
if s.hlAttrDef == nil {
|
|
s.hlAttrDef = h
|
|
}
|
|
|
|
// // Update all cell's highlight
|
|
// // It looks like we don't need it anymore.
|
|
// isUpdateBg := true
|
|
// curwin, ok := s.getWindow(s.ws.cursor.gridid)
|
|
// if ok {
|
|
// isUpdateBg = !curwin.background.equals(s.ws.background)
|
|
// }
|
|
// if isUpdateBg {
|
|
// s.windows.Range(func(_, winITF interface{}) bool {
|
|
// win := winITF.(*Window)
|
|
// if win == nil {
|
|
// return true
|
|
// }
|
|
// if !win.isShown() {
|
|
// return true
|
|
// }
|
|
// if win.content == nil {
|
|
// return true
|
|
// }
|
|
// for _, line := range win.content {
|
|
// for _, cell := range line {
|
|
// if cell != nil {
|
|
// cell.highlight = s.hlAttrDef[cell.highlight.id]
|
|
// }
|
|
// }
|
|
// }
|
|
// return true
|
|
// })
|
|
// }
|
|
}
|
|
|
|
func (s *Screen) setHighlightGroup(args []interface{}) {
|
|
for _, arg := range args {
|
|
a := arg.([]interface{})
|
|
hlName := a[0].(string)
|
|
hlIndex := util.ReflectToInt(a[1])
|
|
s.highlightGroup[hlName] = hlIndex
|
|
}
|
|
}
|
|
|
|
func (s *Screen) makeHighlight(args interface{}) *Highlight {
|
|
arg := args.([]interface{})
|
|
highlight := Highlight{}
|
|
|
|
hl := arg[1].(map[string]interface{})
|
|
info := make(map[string]interface{})
|
|
for _, arg := range arg[3].([]interface{}) {
|
|
info = arg.(map[string]interface{})
|
|
break
|
|
}
|
|
|
|
kind, ok := info["kind"]
|
|
if ok {
|
|
highlight.kind = kind.(string)
|
|
}
|
|
|
|
id, ok := info["id"]
|
|
if ok {
|
|
highlight.id = util.ReflectToInt(id)
|
|
}
|
|
|
|
uiName, ok := info["ui_name"]
|
|
if ok {
|
|
highlight.uiName = uiName.(string)
|
|
}
|
|
|
|
hlName, ok := info["hi_name"]
|
|
if ok {
|
|
highlight.hlName = hlName.(string)
|
|
}
|
|
|
|
italic := hl["italic"]
|
|
if italic != nil {
|
|
highlight.italic = true
|
|
} else {
|
|
highlight.italic = false
|
|
}
|
|
|
|
bold := hl["bold"]
|
|
if bold != nil {
|
|
highlight.bold = true
|
|
} else {
|
|
highlight.bold = false
|
|
}
|
|
|
|
underline := hl["underline"]
|
|
if underline != nil {
|
|
highlight.underline = true
|
|
} else {
|
|
highlight.underline = false
|
|
}
|
|
|
|
undercurl := hl["undercurl"]
|
|
if undercurl != nil {
|
|
highlight.undercurl = true
|
|
} else {
|
|
highlight.undercurl = false
|
|
}
|
|
|
|
underdouble := hl["underdouble"]
|
|
if underdouble != nil {
|
|
highlight.underdouble = true
|
|
} else {
|
|
highlight.underdouble = false
|
|
}
|
|
|
|
underdashed := hl["underdashed"]
|
|
if underdashed != nil {
|
|
highlight.underdashed = true
|
|
} else {
|
|
highlight.underdashed = false
|
|
}
|
|
|
|
underdotted := hl["underdotted"]
|
|
if underdotted != nil {
|
|
highlight.underdotted = true
|
|
} else {
|
|
highlight.underdotted = false
|
|
}
|
|
|
|
strikethrough := hl["strikethrough"]
|
|
if strikethrough != nil {
|
|
highlight.strikethrough = true
|
|
} else {
|
|
highlight.strikethrough = false
|
|
}
|
|
|
|
reverse := hl["reverse"]
|
|
if reverse != nil {
|
|
highlight.reverse = true
|
|
} else {
|
|
highlight.reverse = false
|
|
}
|
|
|
|
fg, ok := hl["foreground"]
|
|
if ok {
|
|
rgba := calcColor(util.ReflectToInt(fg))
|
|
highlight.foreground = rgba
|
|
}
|
|
if highlight.foreground == nil {
|
|
highlight.foreground = s.ws.foreground
|
|
}
|
|
|
|
bg, ok := hl["background"]
|
|
if ok {
|
|
rgba := calcColor(util.ReflectToInt(bg))
|
|
highlight.background = rgba
|
|
}
|
|
if highlight.background == nil {
|
|
highlight.background = s.ws.background
|
|
}
|
|
|
|
sp, ok := hl["special"]
|
|
if ok {
|
|
rgba := calcColor(util.ReflectToInt(sp))
|
|
highlight.special = rgba
|
|
}
|
|
|
|
// af, ok := hl["altfont"]
|
|
// if ok {
|
|
// highlight.altfont = af.(string)
|
|
// }
|
|
|
|
bl, ok := hl["blend"]
|
|
if ok {
|
|
highlight.blend = util.ReflectToInt(bl)
|
|
}
|
|
|
|
return &highlight
|
|
}
|
|
|
|
func (s *Screen) getHighlightByHlname(name string) (hl *Highlight) {
|
|
keys := make([]int, 0, len(s.hlAttrDef))
|
|
for k := range s.hlAttrDef {
|
|
if s.hlAttrDef[k].hlName == "" {
|
|
continue
|
|
}
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
|
|
|
|
for _, k := range keys {
|
|
h := s.hlAttrDef[k]
|
|
if h.hlName == name {
|
|
hl = h
|
|
break
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Screen) getHighlightByUiname(name string) (hl *Highlight) {
|
|
keys := make([]int, 0, len(s.hlAttrDef))
|
|
for k := range s.hlAttrDef {
|
|
if s.hlAttrDef[k].uiName == "" {
|
|
continue
|
|
}
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
|
|
|
|
for _, k := range keys {
|
|
h := s.hlAttrDef[k]
|
|
if h.uiName == name {
|
|
hl = h
|
|
break
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Screen) gridClear(args []interface{}) {
|
|
var gridid gridId
|
|
for _, arg := range args {
|
|
gridid = util.ReflectToInt(arg.([]interface{})[0])
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
win.content = make([][]*Cell, win.rows)
|
|
win.contentMask = make([][]bool, win.rows)
|
|
win.contentMaskOld = make([][]bool, win.rows)
|
|
win.lenLine = make([]int, win.rows)
|
|
win.lenContent = make([]int, win.rows)
|
|
|
|
for i := 0; i < win.rows; i++ {
|
|
win.content[i] = make([]*Cell, win.cols)
|
|
win.contentMask[i] = make([]bool, win.cols)
|
|
win.contentMaskOld[i] = make([]bool, win.cols)
|
|
for j := 0; j < win.cols; j++ {
|
|
win.contentMask[i][j] = true
|
|
}
|
|
win.lenContent[i] = win.cols - 1
|
|
}
|
|
|
|
for i, line := range win.content {
|
|
for j, _ := range line {
|
|
win.content[i][j] = &Cell{
|
|
highlight: win.s.hlAttrDef[0],
|
|
char: " ",
|
|
normalWidth: false,
|
|
covered: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
win.queueRedrawAll()
|
|
}
|
|
}
|
|
|
|
func (s *Screen) gridLine(args []interface{}) {
|
|
var win *Window
|
|
var ok bool
|
|
|
|
for _, arg := range args {
|
|
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
gridArgs := arg.([]interface{})
|
|
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
if win == nil || win.grid != gridid {
|
|
win, ok = s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
}
|
|
|
|
win.updateGridContent(
|
|
util.ReflectToInt(gridArgs[1]),
|
|
util.ReflectToInt(gridArgs[2]),
|
|
gridArgs[3].([]interface{}),
|
|
)
|
|
|
|
s.lastGridLineGrid = win.grid
|
|
}
|
|
}
|
|
|
|
func (s *Screen) gridScroll(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
win.scrollRegion[0] = util.ReflectToInt(arg.([]interface{})[1]) // top
|
|
win.scrollRegion[1] = util.ReflectToInt(arg.([]interface{})[2]) - 1 // bot
|
|
win.scrollRegion[2] = util.ReflectToInt(arg.([]interface{})[3]) // left
|
|
win.scrollRegion[3] = util.ReflectToInt(arg.([]interface{})[4]) - 1 // right
|
|
|
|
win.scroll(
|
|
util.ReflectToInt(arg.([]interface{})[5]), // rows
|
|
)
|
|
}
|
|
}
|
|
|
|
// In Neovim's multigrid UI architecture, a window is represented by multiple independent grids,
|
|
// and grid1 is a special grid called the global grid, which displays window splits, tabs, etc.
|
|
// This is problematic when applying transparency effects to the entire application.
|
|
// If each grid is displayed transparently, the information in global grid behind it will show through,
|
|
// but the current UI specification expects the global grid to be covered by other grids,
|
|
// so there will be unwanted content left over before and after updates.
|
|
// detectCoveredCellInGlobalgrid() logically controls this and detects information
|
|
// about the covered state of each grid in the global grid. These coverage states are used to control
|
|
// whether or not certain cell contents should be drawn when processing the painting of the global grid.
|
|
func (s *Screen) detectCoveredCellInGlobalgrid() {
|
|
if editor.config.Editor.Transparent == 1.0 {
|
|
return
|
|
}
|
|
gwin, ok := s.getWindow(1)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
for _, line := range gwin.content {
|
|
for _, cell := range line {
|
|
if cell == nil {
|
|
continue
|
|
}
|
|
cell.covered = false
|
|
}
|
|
}
|
|
|
|
font := s.font
|
|
|
|
s.windows.Range(func(grid, winITF interface{}) bool {
|
|
|
|
win := winITF.(*Window)
|
|
if win == nil {
|
|
return true
|
|
}
|
|
if win.isFloatWin {
|
|
return true
|
|
}
|
|
if win.isExternal {
|
|
return true
|
|
}
|
|
if win.isMsgGrid {
|
|
return true
|
|
}
|
|
if win.isGridDirty {
|
|
return true
|
|
}
|
|
if win.grid == 1 {
|
|
return true
|
|
}
|
|
if win.IsVisible() {
|
|
x := int(math.Floor(float64(win.pos[0]) * font.cellwidth))
|
|
y := win.pos[1] * font.lineHeight
|
|
width := int(math.Ceil(float64(win.cols) * font.cellwidth))
|
|
height := win.rows * font.lineHeight
|
|
|
|
winrect := core.NewQRect4(
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
)
|
|
|
|
for i, line := range gwin.content {
|
|
for j, cell := range line {
|
|
|
|
if cell == nil {
|
|
continue
|
|
}
|
|
|
|
p := core.NewQPoint2(
|
|
int(font.cellwidth*float64(j)+font.cellwidth/2.0),
|
|
font.lineHeight*i+int(float64(font.lineHeight)/2.0),
|
|
)
|
|
if winrect.Contains(p, true) {
|
|
if !cell.covered {
|
|
cell.covered = true
|
|
gwin.contentMask[i][j] = true
|
|
}
|
|
}
|
|
|
|
// v := 0
|
|
// if cell.covered {
|
|
// v = 1
|
|
// }
|
|
// fmt.Printf("%d", v)
|
|
}
|
|
|
|
// fmt.Printf("\n")
|
|
}
|
|
}
|
|
gwin.queueRedraw(0, 0, gwin.cols, gwin.rows)
|
|
|
|
return true
|
|
})
|
|
|
|
}
|
|
|
|
func (s *Screen) update() {
|
|
s.windows.Range(func(grid, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
// if grid is dirty, we remove this grid
|
|
if win.isGridDirty && !win.isSameAsCursorGrid() {
|
|
// if win.queueRedrawArea[2] > 0 || win.queueRedrawArea[3] > 0 {
|
|
// // If grid has an update area even though it has a dirty flag,
|
|
// // it will still not be removed as a valid grid
|
|
// win.isGridDirty = false
|
|
// } else {
|
|
// // Remove dirty grid
|
|
// win.hide()
|
|
// s.windows.Delete(grid)
|
|
// }
|
|
win.hide()
|
|
win.deleteExternalWin()
|
|
s.deleteWindow(win.grid)
|
|
}
|
|
if win != nil {
|
|
// Fill entire background if background color changed
|
|
if !win.background.equals(s.ws.background) {
|
|
win.background = s.ws.background.copy()
|
|
win.fill()
|
|
}
|
|
|
|
// Suppressed an issue where the cursor remains
|
|
// when moving the cursor in PopOS environment.
|
|
if runtime.GOOS == "linux" {
|
|
if s.ws.viewport == s.ws.oldViewport {
|
|
_, col := win.s.ws.cursor.getRowAndColFromScreen()
|
|
font := win.getFont()
|
|
x := int(math.Ceil(float64(col) * font.cellwidth))
|
|
win.Update2(
|
|
x, 0,
|
|
int(math.Ceil(font.cellwidth)),
|
|
s.height,
|
|
)
|
|
}
|
|
}
|
|
|
|
win.update()
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *Screen) refresh() {
|
|
s.windows.Range(func(grid, winITF interface{}) bool {
|
|
win := winITF.(*Window)
|
|
if win != nil {
|
|
// Fill entire background if background color changed
|
|
if !win.background.equals(s.ws.background) {
|
|
win.background = s.ws.background.copy()
|
|
}
|
|
win.fill()
|
|
win.Update()
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *Screen) runeTextWidth(font *Font, text string) float64 {
|
|
cjk := 0
|
|
ascii := 0
|
|
var buffer bytes.Buffer
|
|
for _, c := range []rune(text) {
|
|
if c >= 0 && c <= 127 {
|
|
ascii++
|
|
} else {
|
|
buffer.WriteString(string(c))
|
|
}
|
|
}
|
|
|
|
r := buffer.String()
|
|
width := (font.cellwidth)*float64(ascii) + (font.cellwidth)*float64(cjk)*2
|
|
if r == "" {
|
|
width += font.fontMetrics.HorizontalAdvance(r, -1)
|
|
}
|
|
if width == 0 {
|
|
width = font.cellwidth * 2
|
|
}
|
|
|
|
return width
|
|
}
|
|
|
|
func (s *Screen) windowPosition(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
id := arg.([]interface{})[1].(nvim.Window)
|
|
row := util.ReflectToInt(arg.([]interface{})[2])
|
|
col := util.ReflectToInt(arg.([]interface{})[3])
|
|
cols := util.ReflectToInt(arg.([]interface{})[4])
|
|
rows := util.ReflectToInt(arg.([]interface{})[5])
|
|
|
|
// fmt.Println("win_pos::", "grid:", gridid, col, row)
|
|
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
s.resizeWindow(gridid, cols, rows)
|
|
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// winbar := s.ws.getOpWinbar(nvim.LocalScope, id)
|
|
win.updateMutex.Lock()
|
|
win.id = id
|
|
win.pos = [2]int{col, row}
|
|
win.setOptions()
|
|
|
|
win.isFloatWin = false
|
|
if win.isExternal {
|
|
win.isExternal = false
|
|
win.deleteExternalWin()
|
|
win.SetParent(s.widget)
|
|
win.raise()
|
|
}
|
|
|
|
win.updateMutex.Unlock()
|
|
|
|
win.move(col, row)
|
|
win.show()
|
|
}
|
|
}
|
|
|
|
func (s *Screen) gridDestroy(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
// NOTE: what should we actually do in the event ??
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
win.isGridDirty = true
|
|
}
|
|
|
|
// Redraw each displayed window.Because shadows leave dust before and after float window drawing.
|
|
s.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.isGridDirty {
|
|
return true
|
|
}
|
|
if win.isShown() {
|
|
win.queueRedrawAll()
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *Screen) windowFloatPosition(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
win.updateMutex.Lock()
|
|
win.id = arg.([]interface{})[1].(nvim.Window)
|
|
win.anchor = arg.([]interface{})[2].(string)
|
|
win.anchorGrid = util.ReflectToInt(arg.([]interface{})[3])
|
|
win.anchorCol = int(util.ReflectToFloat(arg.([]interface{})[5]))
|
|
win.anchorRow = int(util.ReflectToFloat(arg.([]interface{})[4]))
|
|
// focusable := (arg.([]interface{})[6]).(bool)
|
|
|
|
if len(arg.([]interface{})) >= 8 {
|
|
win.zindex.value = int(util.ReflectToInt(arg.([]interface{})[7]))
|
|
win.zindex.order = globalOrder
|
|
globalOrder++
|
|
}
|
|
|
|
editor.putLog("float window generated:", "anchorgrid", win.anchorGrid, "anchor", win.anchor, "anchorCol", win.anchorCol, "anchorRow", win.anchorRow)
|
|
|
|
shouldStackPerZIndex := !win.IsVisible()
|
|
if !win.isFloatWin {
|
|
win.isFloatWin = true
|
|
shouldStackPerZIndex = shouldStackPerZIndex || true
|
|
}
|
|
|
|
if win.isExternal {
|
|
win.deleteExternalWin()
|
|
win.isExternal = false
|
|
}
|
|
|
|
anchorwin, ok := s.getWindow(win.anchorGrid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
win.anchorwin = anchorwin
|
|
win.setOptions()
|
|
|
|
anchorwinIsExternal := win.anchorwin.isExternal
|
|
|
|
win.updateMutex.Unlock()
|
|
|
|
if anchorwinIsExternal {
|
|
win.SetParent(anchorwin)
|
|
}
|
|
|
|
win.setFloatWindowPosition()
|
|
|
|
win.move(win.pos[0], win.pos[1], win.anchorwin)
|
|
|
|
if shouldStackPerZIndex {
|
|
win.raise()
|
|
}
|
|
|
|
win.setShadow()
|
|
win.show()
|
|
|
|
// Redraw anchor window.Because shadows leave dust before and after float window drawing.
|
|
anchorwin.queueRedrawAll()
|
|
}
|
|
}
|
|
|
|
func (s *Screen) windowExternalPosition(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
// winid := arg.([]interface{})[1].(nvim.Window)
|
|
|
|
s.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 == gridid && !win.isExternal {
|
|
win.isExternal = true
|
|
|
|
extwin := createExternalWin()
|
|
win.SetParent(extwin)
|
|
extwin.ConnectKeyPressEvent(editor.keyPress)
|
|
extwin.ConnectKeyReleaseEvent(editor.keyRelease)
|
|
extwin.SetAttribute(core.Qt__WA_InputMethodEnabled, true)
|
|
extwin.ConnectInputMethodEvent(s.ws.InputMethodEvent)
|
|
extwin.ConnectInputMethodQuery(s.ws.InputMethodQuery)
|
|
extwin.ConnectMousePressEvent(win.mouseEvent)
|
|
extwin.ConnectMouseReleaseEvent(win.mouseEvent)
|
|
|
|
extwin.InstallEventFilter(extwin)
|
|
extwin.ConnectEventFilter(func(watched *core.QObject, event *core.QEvent) bool {
|
|
switch event.Type() {
|
|
case core.QEvent__ActivationChange:
|
|
if extwin.IsActiveWindow() {
|
|
editor.isExtWinNowActivated = true
|
|
editor.isExtWinNowInactivated = false
|
|
} else if !extwin.IsActiveWindow() {
|
|
editor.isExtWinNowActivated = false
|
|
editor.isExtWinNowInactivated = true
|
|
}
|
|
default:
|
|
}
|
|
return extwin.EventFilterDefault(watched, event)
|
|
})
|
|
|
|
win.background = s.ws.background.copy()
|
|
extwin.SetAutoFillBackground(true)
|
|
p := gui.NewQPalette()
|
|
p.SetColor2(gui.QPalette__Background, s.ws.background.QColor())
|
|
extwin.SetPalette(p)
|
|
|
|
extwin.Show()
|
|
win.extwin = extwin
|
|
font := win.getFont()
|
|
win.extwin.ConnectMoveEvent(func(ev *gui.QMoveEvent) {
|
|
if win.extwin == nil {
|
|
return
|
|
}
|
|
if ev == nil {
|
|
return
|
|
}
|
|
pos := ev.Pos()
|
|
if pos == nil {
|
|
return
|
|
}
|
|
gPos := editor.window.Pos()
|
|
win.pos[0] = int(float64(pos.X()-gPos.X()) / font.cellwidth)
|
|
win.pos[1] = int(float64(pos.Y()-gPos.Y()) / float64(font.lineHeight))
|
|
})
|
|
width := int(math.Ceil(float64(win.cols) * font.cellwidth))
|
|
height := win.rows * font.lineHeight
|
|
win.setGridGeometry(width, height)
|
|
win.setResizableForExtWin()
|
|
win.move(win.pos[0], win.pos[1])
|
|
win.setOptions()
|
|
win.raise()
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
}
|
|
}
|
|
|
|
func (s *Screen) windowHide(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
if isSkipGlobalId(gridid) {
|
|
continue
|
|
}
|
|
win, ok := s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
win.hide()
|
|
}
|
|
}
|
|
|
|
func (s *Screen) msgSetPos(args []interface{}) {
|
|
for _, arg := range args {
|
|
gridid := util.ReflectToInt(arg.([]interface{})[0])
|
|
row := util.ReflectToInt(arg.([]interface{})[1])
|
|
scrolled := arg.([]interface{})[2].(bool)
|
|
// TODO We should imprement to drawing msgSepChar
|
|
// sepChar := arg.([]interface{})[3].(string)
|
|
|
|
var win *Window
|
|
var ok bool
|
|
win, ok = s.getWindow(gridid)
|
|
// If message grid does not exist, create it
|
|
if !ok {
|
|
gwin, okok := s.getWindow(1)
|
|
if !okok {
|
|
continue
|
|
}
|
|
s.resizeWindow(gridid, gwin.cols, gwin.rows)
|
|
win, ok = s.getWindow(gridid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
}
|
|
win.isMsgGrid = true
|
|
win.isFloatWin = true
|
|
win.zindex.value = 200
|
|
win.pos[1] = row
|
|
win.move(win.pos[0], win.pos[1])
|
|
win.show()
|
|
if scrolled {
|
|
win.raise() // Fix #111
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Screen) windowClose() {
|
|
// Close the window.
|
|
}
|
|
|
|
func isSkipGlobalId(id gridId) bool {
|
|
if editor.config.Editor.SkipGlobalId {
|
|
if id == 1 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|