2021-05-02 18:08:37 +09:00
|
|
|
package editor
|
|
|
|
|
|
|
|
import (
|
2021-05-18 21:29:21 +09:00
|
|
|
"bytes"
|
|
|
|
"errors"
|
2021-05-02 18:08:37 +09:00
|
|
|
"fmt"
|
|
|
|
"math"
|
2021-09-20 01:20:41 +09:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
2021-10-08 23:49:45 +09:00
|
|
|
"sync"
|
2021-05-18 21:29:21 +09:00
|
|
|
"time"
|
2021-05-02 18:08:37 +09:00
|
|
|
"unicode"
|
2021-05-18 21:29:21 +09:00
|
|
|
"unsafe"
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
"github.com/akiyosi/goneovim/util"
|
|
|
|
"github.com/bluele/gcache"
|
|
|
|
"github.com/neovim/go-client/nvim"
|
|
|
|
"github.com/therecipe/qt/core"
|
|
|
|
"github.com/therecipe/qt/gui"
|
|
|
|
"github.com/therecipe/qt/widgets"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
EXTWINBORDERSIZE = 5
|
|
|
|
EXTWINMARGINSIZE = 10
|
|
|
|
)
|
|
|
|
|
|
|
|
type gridId = int
|
|
|
|
|
|
|
|
// Highlight is
|
|
|
|
type Highlight struct {
|
|
|
|
id int
|
|
|
|
kind string
|
|
|
|
uiName string
|
|
|
|
hlName string
|
|
|
|
foreground *RGBA
|
|
|
|
background *RGBA
|
|
|
|
special *RGBA
|
|
|
|
reverse bool
|
|
|
|
italic bool
|
|
|
|
bold bool
|
|
|
|
underline bool
|
|
|
|
undercurl bool
|
|
|
|
blend int
|
|
|
|
strikethrough bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// HlChars is used in screen cache
|
|
|
|
type HlChars struct {
|
|
|
|
text string
|
|
|
|
fg *RGBA
|
|
|
|
// bg *RGBA
|
|
|
|
italic bool
|
|
|
|
bold bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// HlDecoration is used in screen cache
|
|
|
|
type HlDecoration struct {
|
|
|
|
fg *RGBA
|
|
|
|
underline bool
|
|
|
|
undercurl bool
|
|
|
|
strikethrough bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cell is
|
|
|
|
type Cell struct {
|
|
|
|
normalWidth bool
|
|
|
|
char string
|
|
|
|
highlight *Highlight
|
2021-10-08 23:49:45 +09:00
|
|
|
isUpdateBg bool
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
type IntInt [2]int
|
|
|
|
|
|
|
|
// ExternalWin is
|
|
|
|
type ExternalWin struct {
|
|
|
|
widgets.QDialog
|
|
|
|
}
|
|
|
|
|
|
|
|
// Window is
|
|
|
|
type Window struct {
|
|
|
|
widgets.QWidget
|
|
|
|
_ float64 `property:"scrollDiff"`
|
|
|
|
|
2021-10-13 22:17:13 +09:00
|
|
|
snapshot *gui.QPixmap
|
|
|
|
mouseEventState core.QEvent__Type
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
paintMutex sync.RWMutex
|
|
|
|
redrawMutex sync.Mutex
|
|
|
|
updateMutex sync.RWMutex
|
|
|
|
|
|
|
|
doErase bool
|
|
|
|
|
|
|
|
s *Screen
|
|
|
|
content [][]*Cell
|
|
|
|
lenLine []int
|
|
|
|
lenContent []int
|
|
|
|
lenOldContent []int
|
|
|
|
maxLenContent int
|
|
|
|
contentMask [][]bool
|
|
|
|
contentMaskOld [][]bool
|
|
|
|
|
|
|
|
grid gridId
|
|
|
|
isGridDirty bool
|
|
|
|
id nvim.Window
|
|
|
|
bufName string
|
|
|
|
pos [2]int
|
|
|
|
anchor string
|
|
|
|
cols int
|
|
|
|
rows int
|
|
|
|
cwd string
|
|
|
|
ts int
|
|
|
|
wb int
|
|
|
|
ft string
|
|
|
|
|
|
|
|
propMutex sync.RWMutex
|
|
|
|
isMsgGrid bool
|
|
|
|
isFloatWin bool
|
|
|
|
isExternal bool
|
|
|
|
isPopupmenu bool
|
|
|
|
|
|
|
|
scrollRegion []int
|
|
|
|
queueRedrawArea [4]int
|
|
|
|
|
2021-05-27 17:38:37 +09:00
|
|
|
scrollPixels [2]int // for touch pad scroll
|
|
|
|
scrollPixels2 int // for viewport
|
2021-05-02 18:08:37 +09:00
|
|
|
scrollPixelsDeltaY int
|
2021-05-23 01:19:05 +09:00
|
|
|
lastScrollphase core.Qt__ScrollPhase
|
2021-05-02 18:08:37 +09:00
|
|
|
scrollCols int
|
2021-05-27 17:38:37 +09:00
|
|
|
scrollViewport [2][5]int // 1. topline, botline, curline, curcol, grid, 2. oldtopline, oldbotline, oldcurline, oldcurcol, oldgrid
|
2021-05-09 02:12:36 +09:00
|
|
|
doGetSnapshot bool
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
devicePixelRatio float64
|
|
|
|
fgCache Cache
|
|
|
|
|
|
|
|
extwin *ExternalWin
|
|
|
|
extwinConnectResizable bool
|
|
|
|
extwinResized bool
|
|
|
|
extwinManualResized bool
|
|
|
|
extwinAutoLayoutPosX []int
|
|
|
|
extwinAutoLayoutPosY []int
|
|
|
|
extwinRelativePos [2]int
|
|
|
|
|
|
|
|
font *Font
|
|
|
|
background *RGBA
|
|
|
|
width float64
|
|
|
|
height int
|
|
|
|
localWindows *[4]localWindow
|
|
|
|
}
|
|
|
|
|
|
|
|
type localWindow struct {
|
|
|
|
grid gridId
|
|
|
|
isResized bool
|
|
|
|
localWidth float64
|
|
|
|
localHeight int
|
|
|
|
}
|
|
|
|
|
|
|
|
func purgeQimage(key, value interface{}) {
|
|
|
|
image := value.(*gui.QImage)
|
|
|
|
image.DestroyQImage()
|
|
|
|
}
|
|
|
|
|
|
|
|
func newCache() Cache {
|
|
|
|
g := gcache.New(editor.config.Editor.CacheSize).LRU().
|
|
|
|
EvictedFunc(purgeQimage).
|
|
|
|
PurgeVisitorFunc(purgeQimage).
|
|
|
|
Build()
|
|
|
|
return *(*Cache)(unsafe.Pointer(&g))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cache) set(key, value interface{}) error {
|
|
|
|
return c.Set(key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cache) get(key interface{}) (interface{}, error) {
|
|
|
|
return c.Get(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cache) purge() {
|
|
|
|
c.Purge()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) paint(event *gui.QPaintEvent) {
|
|
|
|
w.paintMutex.Lock()
|
|
|
|
|
|
|
|
p := gui.NewQPainter2(w)
|
|
|
|
if w.doErase {
|
|
|
|
p.EraseRect3(w.Rect())
|
|
|
|
p.DestroyQPainter()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
font := w.getFont()
|
|
|
|
|
|
|
|
// Set devicePixelRatio if it is not set
|
|
|
|
if w.devicePixelRatio == 0 {
|
|
|
|
w.devicePixelRatio = float64(p.PaintEngine().PaintDevice().DevicePixelRatio())
|
|
|
|
}
|
|
|
|
|
|
|
|
rect := event.Rect()
|
|
|
|
col := int(float64(rect.Left()) / font.truewidth)
|
|
|
|
row := int(float64(rect.Top()) / float64(font.lineHeight))
|
|
|
|
cols := int(math.Ceil(float64(rect.Width()) / font.truewidth))
|
|
|
|
rows := int(math.Ceil(float64(rect.Height()) / float64(font.lineHeight)))
|
|
|
|
|
|
|
|
// Draw contents
|
|
|
|
for y := row; y < row+rows; y++ {
|
|
|
|
if y >= w.rows {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
w.drawBackground(p, y, col, cols)
|
|
|
|
w.drawForeground(p, y, col, cols)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw scroll snapshot
|
|
|
|
// TODO: If there are wrapped lines in the viewport, the snapshot will be misaligned.
|
|
|
|
w.drawScrollSnapshot(p)
|
|
|
|
|
|
|
|
// TODO: We should use msgSepChar to separate message window area
|
|
|
|
// // If Window is Message Area, draw separator
|
|
|
|
// if w.isMsgGrid {
|
|
|
|
// w.drawMsgSeparator(p)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Draw indent guide
|
|
|
|
if editor.config.Editor.IndentGuide {
|
|
|
|
w.drawIndentguide(p, row, rows)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw float window border
|
|
|
|
if w.isFloatWin {
|
|
|
|
w.drawFloatWindowBorder(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw vim window separator
|
|
|
|
w.drawWindowSeparators(p, row, col, rows, cols)
|
|
|
|
|
|
|
|
// Reset to 0 after drawing is complete.
|
|
|
|
// This is to suppress flickering in smooth scroll
|
|
|
|
dx := math.Abs(float64(w.scrollPixels[0]))
|
|
|
|
dy := math.Abs(float64(w.scrollPixels[1]))
|
|
|
|
if dx >= font.truewidth {
|
|
|
|
w.scrollPixels[0] = 0
|
|
|
|
}
|
|
|
|
if dy >= float64(font.lineHeight) {
|
|
|
|
w.scrollPixels[1] = 0
|
|
|
|
}
|
|
|
|
|
2021-05-23 01:19:05 +09:00
|
|
|
if w.lastScrollphase == core.Qt__NoScrollPhase {
|
|
|
|
w.lastScrollphase = core.Qt__ScrollEnd
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
p.DestroyQPainter()
|
|
|
|
|
|
|
|
w.paintMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawScrollSnapshot(p *gui.QPainter) {
|
|
|
|
if !editor.config.Editor.SmoothScroll {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if w.s.name == "minimap" {
|
|
|
|
return
|
|
|
|
}
|
2021-05-07 00:17:43 +09:00
|
|
|
if w.snapshot == nil || editor.isKeyAutoRepeating {
|
2021-05-02 18:08:37 +09:00
|
|
|
return
|
|
|
|
}
|
2021-07-08 21:54:39 +09:00
|
|
|
if w.scrollPixels2 == 0 {
|
|
|
|
return
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
font := w.getFont()
|
|
|
|
height := w.scrollCols * font.lineHeight
|
|
|
|
snapshotPos := 0
|
|
|
|
if w.scrollPixels2 > 0 {
|
|
|
|
snapshotPos = w.scrollPixels2 - height
|
|
|
|
} else if w.scrollPixels2 < 0 {
|
|
|
|
snapshotPos = height + w.scrollPixels2
|
|
|
|
}
|
|
|
|
if w.scrollPixels2 != 0 {
|
|
|
|
p.DrawPixmap9(
|
|
|
|
0,
|
|
|
|
snapshotPos,
|
2021-05-07 00:17:43 +09:00
|
|
|
w.snapshot,
|
2021-05-02 18:08:37 +09:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) getFont() *Font {
|
|
|
|
if w.font == nil {
|
|
|
|
return w.s.font
|
|
|
|
}
|
|
|
|
|
|
|
|
return w.font
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) getTS() int {
|
2021-08-13 18:01:53 +09:00
|
|
|
var ts int
|
|
|
|
var ok bool
|
|
|
|
if w.id != 0 {
|
|
|
|
ts, ok = w.s.ws.windowsTs[w.id]
|
|
|
|
if ok {
|
|
|
|
return ts
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
return w.ts
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawIndentguide(p *gui.QPainter, row, rows int) {
|
|
|
|
if w == nil {
|
|
|
|
return
|
|
|
|
}
|
2021-08-13 18:01:53 +09:00
|
|
|
if w.grid == 1 || w.isMsgGrid {
|
|
|
|
return
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
if w.s.name == "minimap" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if w.isMsgGrid {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if w.ft == "" {
|
2021-07-04 23:31:10 +09:00
|
|
|
w.s.ws.optionsetMutex.Lock()
|
|
|
|
w.ft = w.s.ws.windowsFt[w.id]
|
|
|
|
w.s.ws.optionsetMutex.Unlock()
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
if !w.isShown() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ts := w.getTS()
|
|
|
|
if ts <= 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
headspaceOfRows := make(map[int]int)
|
|
|
|
for y := row; y < rows; y++ {
|
|
|
|
if y+1 >= len(w.content) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
l, _ := w.countHeadSpaceOfLine(y)
|
|
|
|
headspaceOfRows[y] = l
|
|
|
|
}
|
|
|
|
|
|
|
|
drawIndents := make(map[IntInt]bool)
|
|
|
|
for y := row; y < rows; y++ {
|
|
|
|
if y+1 >= len(w.content) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// nextline := w.content[y+1]
|
|
|
|
line := w.content[y]
|
|
|
|
res := 0
|
|
|
|
for x := 0; x < w.maxLenContent; x++ {
|
|
|
|
if x+1 >= len(line) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if line[x+1] == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c := line[x]
|
|
|
|
if c == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if c.isSignColumn() {
|
|
|
|
res++
|
|
|
|
}
|
|
|
|
if c.char != " " && !c.isSignColumn() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// yylen, _ := w.countHeadSpaceOfLine(y)
|
|
|
|
yylen := headspaceOfRows[y]
|
|
|
|
if x > res && (x+1-res)%ts == 0 {
|
|
|
|
ylen := x + 1
|
|
|
|
|
|
|
|
if ylen > yylen {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
doPaintIndent := false
|
|
|
|
for mm := y; mm < len(w.content); mm++ {
|
|
|
|
if drawIndents[[2]int{x + 1, mm}] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// mmlen, _ := w.countHeadSpaceOfLine(mm)
|
|
|
|
mmlen := headspaceOfRows[mm]
|
|
|
|
|
|
|
|
if mmlen == ylen {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if mmlen > ylen && w.lenLine[mm] > res {
|
|
|
|
doPaintIndent = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if mmlen == w.cols && !doPaintIndent {
|
|
|
|
for nn := mm + 1; nn < len(w.content); nn++ {
|
|
|
|
// nnlen, _ := w.countHeadSpaceOfLine(nn)
|
|
|
|
nnlen := headspaceOfRows[nn]
|
|
|
|
if nnlen == ylen {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if nnlen < ylen {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if nnlen > ylen && w.lenLine[nn] > res {
|
|
|
|
doPaintIndent = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mmlen < ylen {
|
|
|
|
doBreak := true
|
|
|
|
// If the line to draw an indent-guide has a wrapped line
|
|
|
|
// in the next line, do not skip drawing
|
|
|
|
// TODO: We do not detect the wraped line when `:set nonu` setting.
|
|
|
|
if mm+1 < len(w.content) {
|
|
|
|
// lllen, _ := w.countHeadSpaceOfLine(mm+1)
|
|
|
|
lllen := headspaceOfRows[mm+1]
|
|
|
|
if mm >= 0 {
|
|
|
|
if lllen > ylen {
|
|
|
|
for xx := 0; xx < w.lenLine[mm]; xx++ {
|
|
|
|
if xx >= len(w.content[mm]) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if w.content[mm][xx] == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if w.content[mm][xx].highlight.hlName == "LineNr" {
|
|
|
|
if w.content[mm][xx].char == " " {
|
|
|
|
doBreak = false
|
|
|
|
} else if w.content[mm][xx].char != " " {
|
|
|
|
doBreak = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if doBreak {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if w.content[mm][x+1] == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if w.content[mm][x+1].char != " " {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !doPaintIndent {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !drawIndents[[2]int{x + 1, mm}] {
|
|
|
|
drawIndents[[2]int{x + 1, mm}] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// detect current block
|
|
|
|
currentBlock := make(map[IntInt]bool)
|
|
|
|
for x := w.s.cursor[1]; x >= 0; x-- {
|
|
|
|
if drawIndents[[2]int{x + 1, w.s.cursor[0]}] {
|
|
|
|
for y := w.s.cursor[0]; y >= 0; y-- {
|
|
|
|
if drawIndents[[2]int{x + 1, y}] {
|
|
|
|
currentBlock[[2]int{x + 1, y}] = true
|
|
|
|
}
|
|
|
|
if !drawIndents[[2]int{x + 1, y}] {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for y := w.s.cursor[0]; y < len(w.content); y++ {
|
|
|
|
if drawIndents[[2]int{x + 1, y}] {
|
|
|
|
currentBlock[[2]int{x + 1, y}] = true
|
|
|
|
}
|
|
|
|
if !drawIndents[[2]int{x + 1, y}] {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw indent guide
|
|
|
|
for y := row; y < len(w.content); y++ {
|
|
|
|
for x := 0; x < w.maxLenContent; x++ {
|
|
|
|
if !drawIndents[[2]int{x + 1, y}] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if currentBlock[[2]int{x + 1, y}] {
|
|
|
|
w.drawIndentline(p, x+1, y, true)
|
|
|
|
} else {
|
|
|
|
w.drawIndentline(p, x+1, y, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawIndentline(p *gui.QPainter, x int, y int, b bool) {
|
|
|
|
font := w.getFont()
|
2021-05-22 16:00:02 +09:00
|
|
|
|
|
|
|
// Set smooth scroll offset
|
2021-05-23 01:19:05 +09:00
|
|
|
scrollPixels := 0
|
|
|
|
if w.lastScrollphase != core.Qt__NoScrollPhase {
|
|
|
|
scrollPixels = w.scrollPixels2
|
|
|
|
}
|
2021-05-22 16:00:02 +09:00
|
|
|
if editor.config.Editor.LineToScroll == 1 {
|
|
|
|
scrollPixels += w.scrollPixels[1]
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
X := float64(x) * font.truewidth
|
2021-05-22 16:00:02 +09:00
|
|
|
Y := float64(y*font.lineHeight) + float64(scrollPixels)
|
2021-05-02 18:08:37 +09:00
|
|
|
var color *RGBA = editor.colors.indentGuide
|
|
|
|
var lineWeight float64 = 1
|
|
|
|
if b {
|
|
|
|
color = warpColor(editor.colors.indentGuide, -40)
|
|
|
|
lineWeight = 1.5
|
|
|
|
}
|
|
|
|
p.FillRect4(
|
|
|
|
core.NewQRectF4(
|
|
|
|
X,
|
|
|
|
Y,
|
|
|
|
lineWeight,
|
|
|
|
float64(font.lineHeight),
|
|
|
|
),
|
|
|
|
color.QColor(),
|
|
|
|
)
|
|
|
|
|
|
|
|
if w.lenContent[y] < x {
|
|
|
|
w.lenContent[y] = x
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawMsgSeparator(p *gui.QPainter) {
|
|
|
|
highNo, ok := w.s.highlightGroup["MsgSeparator"]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
hl, ok := w.s.hlAttrDef[highNo]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if hl == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
color := hl.fg()
|
|
|
|
p.FillRect4(
|
|
|
|
core.NewQRectF4(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
float64(w.Width()),
|
|
|
|
1,
|
|
|
|
),
|
|
|
|
gui.NewQColor3(
|
|
|
|
color.R,
|
|
|
|
color.G,
|
|
|
|
color.B,
|
|
|
|
200),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawFloatWindowBorder(p *gui.QPainter) {
|
|
|
|
if !editor.config.Editor.DrawBorderForFloatWindow {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var color *RGBA
|
|
|
|
highNo, ok := w.s.highlightGroup["GoneovimFloatWindowBorder"]
|
|
|
|
if !ok {
|
|
|
|
color = editor.colors.fg
|
|
|
|
} else {
|
|
|
|
hl, ok := w.s.hlAttrDef[highNo]
|
|
|
|
if !ok || hl == nil {
|
|
|
|
color = editor.colors.fg
|
|
|
|
} else {
|
|
|
|
color = hl.fg()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
width := float64(w.Width())
|
|
|
|
height := float64(w.Height())
|
|
|
|
|
|
|
|
left := core.NewQRectF4(0, 0, 1, height)
|
|
|
|
top := core.NewQRectF4(0, 0, width, 1)
|
|
|
|
right := core.NewQRectF4(width-1, 0, 1, height)
|
|
|
|
bottom := core.NewQRectF4(0, height-1, width, 1)
|
|
|
|
|
|
|
|
p.FillRect4(
|
|
|
|
left,
|
|
|
|
gui.NewQColor3(
|
|
|
|
color.R,
|
|
|
|
color.G,
|
|
|
|
color.B,
|
|
|
|
128),
|
|
|
|
)
|
|
|
|
p.FillRect4(
|
|
|
|
top,
|
|
|
|
gui.NewQColor3(
|
|
|
|
color.R,
|
|
|
|
color.G,
|
|
|
|
color.B,
|
|
|
|
128),
|
|
|
|
)
|
|
|
|
p.FillRect4(
|
|
|
|
right,
|
|
|
|
gui.NewQColor3(
|
|
|
|
color.R,
|
|
|
|
color.G,
|
|
|
|
color.B,
|
|
|
|
128),
|
|
|
|
)
|
|
|
|
p.FillRect4(
|
|
|
|
bottom,
|
|
|
|
gui.NewQColor3(
|
|
|
|
color.R,
|
|
|
|
color.G,
|
|
|
|
color.B,
|
|
|
|
128),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawWindowSeparators(p *gui.QPainter, row, col, rows, cols int) {
|
|
|
|
if w == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if w.grid != 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !editor.config.Editor.DrawWindowSeparator {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
gwin, ok := w.s.getWindow(1)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
gwinrows := gwin.rows
|
|
|
|
|
|
|
|
w.s.windows.Range(func(_, winITF interface{}) bool {
|
|
|
|
win := winITF.(*Window)
|
|
|
|
|
|
|
|
if win == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if !win.isShown() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if win.isFloatWin {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if win.isMsgGrid {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if win.pos[0]+win.cols < row && (win.pos[1]+win.rows+1) < col {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if win.pos[0] > (row+rows) && (win.pos[1]+win.rows) > (col+cols) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
win.drawWindowSeparator(p, gwinrows)
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawWindowSeparator(p *gui.QPainter, gwinrows int) {
|
|
|
|
font := w.getFont()
|
|
|
|
|
|
|
|
// window position is based on cols, rows of global font setting
|
|
|
|
x := int(float64(w.pos[0]) * w.s.font.truewidth)
|
|
|
|
y := w.pos[1] * w.s.font.lineHeight
|
|
|
|
color := editor.colors.windowSeparator
|
|
|
|
width := int(float64(w.cols) * font.truewidth)
|
|
|
|
winHeight := int((float64(w.rows) + 0.92) * float64(font.lineHeight))
|
|
|
|
|
|
|
|
// Vim uses the showtabline option to change the display state of the tabline
|
|
|
|
// based on the number of tabs. We need to look at these states to adjust
|
|
|
|
// the length and display position of the window separator
|
|
|
|
tablineNum := 0
|
|
|
|
numOfTabs := w.s.ws.getNumOfTabs()
|
|
|
|
if numOfTabs > 1 {
|
|
|
|
tablineNum = 1
|
|
|
|
}
|
|
|
|
drawTabline := editor.config.Tabline.Visible && editor.config.Editor.ExtTabline
|
|
|
|
if w.s.ws.showtabline == 2 && drawTabline && numOfTabs == 1 {
|
|
|
|
tablineNum = -1
|
|
|
|
}
|
|
|
|
shift := font.lineHeight / 2
|
|
|
|
if w.rows+w.s.ws.showtabline+tablineNum+1 == gwinrows {
|
|
|
|
winHeight = w.rows * font.lineHeight
|
|
|
|
shift = 0
|
|
|
|
} else {
|
|
|
|
if w.pos[1] == tablineNum {
|
|
|
|
winHeight = w.rows*font.lineHeight + int(float64(font.lineHeight)/2.0)
|
|
|
|
shift = 0
|
|
|
|
}
|
|
|
|
if w.pos[1]+w.rows == gwinrows-2 {
|
|
|
|
winHeight = w.rows*font.lineHeight + int(float64(font.lineHeight)/2.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vertical
|
|
|
|
if y+font.lineHeight+1 < w.s.widget.Height() {
|
|
|
|
p.FillRect5(
|
|
|
|
int(float64(x+width)+font.truewidth/2),
|
|
|
|
y-shift,
|
|
|
|
2,
|
|
|
|
winHeight,
|
|
|
|
color.QColor(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
// vertical gradient
|
|
|
|
if editor.config.Editor.WindowSeparatorGradient {
|
|
|
|
gradient := gui.NewQLinearGradient3(
|
|
|
|
float64(x+width)+font.truewidth/2,
|
|
|
|
0,
|
|
|
|
float64(x+width)+font.truewidth/2-6,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
gradient.SetColorAt(0, gui.NewQColor3(color.R, color.G, color.B, 125))
|
|
|
|
gradient.SetColorAt(1, gui.NewQColor3(color.R, color.G, color.B, 0))
|
|
|
|
brush := gui.NewQBrush10(gradient)
|
|
|
|
p.FillRect2(
|
|
|
|
int(float64(x+width)+font.truewidth/2)-6,
|
|
|
|
y-shift,
|
|
|
|
6,
|
|
|
|
winHeight,
|
|
|
|
brush,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
bottomBorderPos := w.pos[1]*w.s.font.lineHeight + w.Rect().Bottom()
|
|
|
|
isSkipDrawBottomBorder := bottomBorderPos > w.s.bottomWindowPos()-w.s.font.lineHeight && bottomBorderPos < w.s.bottomWindowPos()+w.s.font.lineHeight
|
|
|
|
if isSkipDrawBottomBorder {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Horizontal
|
|
|
|
height := w.rows * font.lineHeight
|
|
|
|
y2 := y + height - 1 + font.lineHeight/2
|
|
|
|
|
|
|
|
p.FillRect5(
|
|
|
|
int(float64(x)-font.truewidth/2),
|
|
|
|
y2,
|
|
|
|
int((float64(w.cols)+0.92)*font.truewidth),
|
|
|
|
2,
|
|
|
|
color.QColor(),
|
|
|
|
)
|
|
|
|
// horizontal gradient
|
|
|
|
if editor.config.Editor.WindowSeparatorGradient {
|
|
|
|
hgradient := gui.NewQLinearGradient3(
|
|
|
|
0,
|
|
|
|
float64(y2),
|
|
|
|
0,
|
|
|
|
float64(y2)-6,
|
|
|
|
)
|
|
|
|
hgradient.SetColorAt(0, gui.NewQColor3(color.R, color.G, color.B, 125))
|
|
|
|
hgradient.SetColorAt(1, gui.NewQColor3(color.R, color.G, color.B, 0))
|
|
|
|
hbrush := gui.NewQBrush10(hgradient)
|
|
|
|
p.FillRect2(
|
|
|
|
int(float64(x)-font.truewidth/2),
|
|
|
|
y2-6,
|
|
|
|
int((float64(w.cols)+0.92)*font.truewidth),
|
|
|
|
6,
|
|
|
|
hbrush,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) wheelEvent(event *gui.QWheelEvent) {
|
|
|
|
var v, h, vert, horiz int
|
2021-10-13 22:17:13 +09:00
|
|
|
var action string
|
2021-05-02 18:08:37 +09:00
|
|
|
|
2021-08-18 22:01:41 +09:00
|
|
|
editor.putLog("start wheel event")
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
font := w.getFont()
|
|
|
|
|
|
|
|
// Detect current mode
|
|
|
|
mode := w.s.ws.mode
|
2021-08-18 17:53:01 +09:00
|
|
|
isTmode := w.s.ws.terminalMode
|
2021-08-18 22:01:41 +09:00
|
|
|
editor.putLog("detect neovim mode:", mode)
|
|
|
|
editor.putLog("detect neovim terminal mode:", fmt.Sprintf("%v", isTmode))
|
2021-08-18 17:53:01 +09:00
|
|
|
if isTmode {
|
2021-05-02 18:08:37 +09:00
|
|
|
w.s.ws.nvim.Input(`<C-\><C-n>`)
|
|
|
|
} else if mode != "normal" {
|
|
|
|
w.s.ws.nvim.Input(w.s.ws.escKeyInInsert)
|
|
|
|
}
|
|
|
|
|
|
|
|
pixels := event.PixelDelta()
|
|
|
|
if pixels != nil {
|
|
|
|
v = pixels.Y()
|
|
|
|
h = pixels.X()
|
|
|
|
}
|
2021-05-23 02:04:49 +09:00
|
|
|
phase := event.Phase()
|
|
|
|
if w.lastScrollphase != phase && w.lastScrollphase != core.Qt__ScrollEnd {
|
|
|
|
w.doGetSnapshot = true
|
|
|
|
}
|
|
|
|
w.lastScrollphase = phase
|
2021-05-23 01:19:05 +09:00
|
|
|
isStopScroll := (w.lastScrollphase == core.Qt__ScrollEnd)
|
2021-05-02 18:08:37 +09:00
|
|
|
|
2021-05-22 16:00:02 +09:00
|
|
|
// Smooth scroll with touchpad
|
2021-05-02 18:08:37 +09:00
|
|
|
if (v == 0 || h == 0) && isStopScroll {
|
|
|
|
vert, horiz = w.smoothUpdate(v, h, isStopScroll)
|
2021-05-27 17:38:37 +09:00
|
|
|
} else if (v != 0 || h != 0) && phase != core.Qt__NoScrollPhase {
|
2021-05-02 18:08:37 +09:00
|
|
|
// If Scrolling has ended, reset the displacement of the line
|
|
|
|
vert, horiz = w.smoothUpdate(v, h, isStopScroll)
|
|
|
|
} else {
|
|
|
|
angles := event.AngleDelta()
|
2021-05-23 01:19:05 +09:00
|
|
|
vert = angles.Y()
|
|
|
|
horiz = angles.X()
|
2021-05-02 18:08:37 +09:00
|
|
|
if event.Inverted() {
|
|
|
|
vert = -1 * vert
|
|
|
|
}
|
|
|
|
// Scroll per 1 line
|
|
|
|
if math.Abs(float64(vert)) > 1 {
|
|
|
|
vert = vert / int(math.Abs(float64(vert)))
|
|
|
|
}
|
|
|
|
if math.Abs(float64(horiz)) > 1 {
|
|
|
|
horiz = horiz / int(math.Abs(float64(horiz)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll acceleration
|
|
|
|
accel := 1
|
|
|
|
if math.Abs(float64(v)) > float64(font.lineHeight) {
|
|
|
|
accel = int(math.Abs(float64(v)) * 2.5 / float64(font.lineHeight))
|
|
|
|
if accel > 6 {
|
|
|
|
accel = 6
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if accel == 0 {
|
|
|
|
accel = 1
|
|
|
|
}
|
|
|
|
vert = vert * int(math.Sqrt(float64(accel)))
|
|
|
|
|
|
|
|
if vert == 0 && horiz == 0 {
|
|
|
|
return
|
|
|
|
}
|
2021-10-13 22:17:13 +09:00
|
|
|
|
2021-05-23 01:19:05 +09:00
|
|
|
if editor.config.Editor.ReversingScrollDirection {
|
|
|
|
if vert < 0 {
|
2021-10-13 22:17:13 +09:00
|
|
|
action = "up"
|
|
|
|
} else if vert > 0 {
|
|
|
|
action = "down"
|
2021-05-23 01:19:05 +09:00
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
} else {
|
2021-05-23 01:19:05 +09:00
|
|
|
if vert > 0 {
|
2021-10-13 22:17:13 +09:00
|
|
|
action = "up"
|
|
|
|
} else if vert < 0 {
|
|
|
|
action = "down"
|
2021-05-23 01:19:05 +09:00
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
2021-10-13 22:17:13 +09:00
|
|
|
if action == "" {
|
|
|
|
if horiz > 0 {
|
|
|
|
action = "left"
|
|
|
|
} else if horiz < 0 {
|
|
|
|
action = "right"
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the window at the mouse pointer is not the current window
|
2021-09-20 01:20:41 +09:00
|
|
|
w.focusGrid()
|
2021-10-13 22:17:13 +09:00
|
|
|
|
|
|
|
mod := editor.modPrefix(event.Modifiers())
|
|
|
|
row := int(float64(event.X()) / font.truewidth)
|
|
|
|
col := int(float64(event.Y()) / float64(font.lineHeight))
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
if w.s.ws.isMappingScrollKey {
|
2021-08-18 22:01:41 +09:00
|
|
|
editor.putLog("detect a mapping to <C-e>, <C-y> keys.")
|
2021-05-02 18:08:37 +09:00
|
|
|
if vert != 0 {
|
2021-10-13 22:17:13 +09:00
|
|
|
w.s.ws.nvim.InputMouse("wheel", action, mod, w.grid, row, col)
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
} else {
|
2021-05-23 01:19:05 +09:00
|
|
|
if editor.config.Editor.ReversingScrollDirection {
|
|
|
|
if vert > 0 {
|
|
|
|
go w.s.ws.nvim.Input(fmt.Sprintf("%v<C-e>", editor.config.Editor.LineToScroll*int(math.Abs(float64(vert)))))
|
|
|
|
} else if vert < 0 {
|
|
|
|
go w.s.ws.nvim.Input(fmt.Sprintf("%v<C-y>", editor.config.Editor.LineToScroll*int(math.Abs(float64(vert)))))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if vert > 0 {
|
|
|
|
go w.s.ws.nvim.Input(fmt.Sprintf("%v<C-y>", editor.config.Editor.LineToScroll*int(math.Abs(float64(vert)))))
|
|
|
|
} else if vert < 0 {
|
|
|
|
go w.s.ws.nvim.Input(fmt.Sprintf("%v<C-e>", editor.config.Editor.LineToScroll*int(math.Abs(float64(vert)))))
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if editor.config.Editor.DisableHorizontalScroll {
|
|
|
|
return
|
|
|
|
}
|
2021-10-13 22:17:13 +09:00
|
|
|
if vert != 0 {
|
2021-05-02 18:08:37 +09:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if horiz != 0 {
|
2021-10-13 22:17:13 +09:00
|
|
|
w.s.ws.nvim.InputMouse("wheel", action, mod, w.grid, row, col)
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
event.Accept()
|
|
|
|
}
|
|
|
|
|
2021-09-20 01:20:41 +09:00
|
|
|
func (w *Window) focusGrid() {
|
|
|
|
// If the window at the mouse pointer is not the current window
|
|
|
|
if w.grid != w.s.ws.cursor.gridid {
|
|
|
|
done := make(chan bool, 2)
|
|
|
|
go func() {
|
|
|
|
_ = w.s.ws.nvim.SetCurrentWindow(w.id)
|
|
|
|
done <- true
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
case <-time.After(40 * time.Millisecond):
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
// screen smooth update with touchpad
|
|
|
|
func (w *Window) smoothUpdate(v, h int, isStopScroll bool) (int, int) {
|
|
|
|
var vert, horiz int
|
|
|
|
font := w.getFont()
|
|
|
|
|
|
|
|
if isStopScroll {
|
|
|
|
w.scrollPixels[0] = 0
|
|
|
|
w.scrollPixels[1] = 0
|
2021-05-27 17:38:37 +09:00
|
|
|
w.queueRedrawAll()
|
|
|
|
w.refreshUpdateArea(1)
|
2021-05-02 18:08:37 +09:00
|
|
|
w.update()
|
|
|
|
w.s.ws.cursor.update()
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if h < 0 && w.scrollPixels[0] > 0 {
|
|
|
|
w.scrollPixels[0] = 0
|
|
|
|
}
|
|
|
|
// if v < 0 && w.scrollPixels[1] > 0 {
|
|
|
|
// w.scrollPixels[1] = 0
|
|
|
|
// }
|
|
|
|
|
|
|
|
dx := math.Abs(float64(w.scrollPixels[0]))
|
|
|
|
dy := math.Abs(float64(w.scrollPixels[1]))
|
|
|
|
|
|
|
|
if dx < font.truewidth {
|
|
|
|
w.scrollPixels[0] += h
|
|
|
|
}
|
|
|
|
if dy < float64(font.lineHeight) {
|
|
|
|
w.scrollPixels[1] += v
|
|
|
|
}
|
|
|
|
|
|
|
|
dx = math.Abs(float64(w.scrollPixels[0]))
|
|
|
|
dy = math.Abs(float64(w.scrollPixels[1]))
|
|
|
|
|
|
|
|
if dx >= font.truewidth {
|
|
|
|
horiz = int(float64(w.scrollPixels[0]) / font.truewidth)
|
|
|
|
}
|
|
|
|
if dy >= float64(font.lineHeight) {
|
|
|
|
vert = int(float64(w.scrollPixels[1]) / float64(font.lineHeight))
|
|
|
|
// NOTE: Reset to 0 after paint event is complete.
|
|
|
|
// This is to suppress flickering.
|
|
|
|
}
|
|
|
|
|
|
|
|
// w.update()
|
|
|
|
// w.s.ws.cursor.update()
|
|
|
|
if !(dx >= font.truewidth || dy > float64(font.lineHeight)) {
|
|
|
|
w.update()
|
|
|
|
w.s.ws.cursor.update()
|
|
|
|
}
|
|
|
|
|
|
|
|
return vert, horiz
|
|
|
|
}
|
|
|
|
|
2021-05-27 17:38:37 +09:00
|
|
|
// smoothscroll makes Neovim's scroll command behavior smooth and animated.
|
|
|
|
func (win *Window) smoothScroll(diff int) {
|
|
|
|
// process smooth scroll
|
|
|
|
a := core.NewQPropertyAnimation2(win, core.NewQByteArray2("scrollDiff", len("scrollDiff")), win)
|
|
|
|
a.ConnectValueChanged(func(value *core.QVariant) {
|
|
|
|
ok := false
|
|
|
|
v := value.ToDouble(&ok)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
font := win.getFont()
|
|
|
|
win.scrollPixels2 = int(float64(diff) * v * float64(font.lineHeight))
|
|
|
|
win.Update2(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
int(float64(win.cols)*font.truewidth),
|
2021-07-08 21:54:39 +09:00
|
|
|
win.rows*font.lineHeight,
|
2021-05-27 17:38:37 +09:00
|
|
|
)
|
|
|
|
if win.scrollPixels2 == 0 {
|
|
|
|
win.doErase = true
|
|
|
|
win.Update2(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
int(float64(win.cols)*font.truewidth),
|
|
|
|
win.cols*font.lineHeight,
|
|
|
|
)
|
|
|
|
win.doErase = false
|
|
|
|
win.fill()
|
|
|
|
|
|
|
|
// get snapshot
|
2021-06-05 14:24:07 +09:00
|
|
|
if !editor.isKeyAutoRepeating && editor.config.Editor.SmoothScroll {
|
2021-05-27 17:38:37 +09:00
|
|
|
win.snapshot = win.Grab(win.Rect())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
a.SetDuration(220)
|
|
|
|
a.SetStartValue(core.NewQVariant10(1))
|
|
|
|
a.SetEndValue(core.NewQVariant10(0))
|
|
|
|
// a.SetEasingCurve(core.NewQEasingCurve(core.QEasingCurve__OutQuart))
|
|
|
|
a.SetEasingCurve(core.NewQEasingCurve(core.QEasingCurve__OutExpo))
|
|
|
|
// a.SetEasingCurve(core.NewQEasingCurve(core.QEasingCurve__OutCirc))
|
|
|
|
a.Start(core.QAbstractAnimation__DeletionPolicy(core.QAbstractAnimation__DeleteWhenStopped))
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
func (hl *Highlight) fg() *RGBA {
|
|
|
|
var color *RGBA
|
|
|
|
if hl.reverse {
|
|
|
|
color = hl.background
|
|
|
|
if color == nil {
|
|
|
|
// color = w.s.ws.background
|
|
|
|
color = editor.colors.bg
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
color = hl.foreground
|
|
|
|
if color == nil {
|
|
|
|
// color = w.s.ws.foreground
|
|
|
|
color = editor.colors.fg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return color
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hl *Highlight) bg() *RGBA {
|
|
|
|
var color *RGBA
|
|
|
|
if hl.reverse {
|
|
|
|
color = hl.foreground
|
|
|
|
if color == nil {
|
|
|
|
// color = w.s.ws.foreground
|
|
|
|
color = editor.colors.fg
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
color = hl.background
|
|
|
|
if color == nil {
|
|
|
|
// color = w.s.ws.background
|
|
|
|
color = editor.colors.bg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return color
|
|
|
|
}
|
|
|
|
|
|
|
|
func (win *Window) updateGridContent(row, colStart int, cells []interface{}) {
|
|
|
|
if colStart < 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if row >= win.rows {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Suppresses flickering during smooth scrolling
|
|
|
|
if win.scrollPixels[1] != 0 {
|
|
|
|
win.scrollPixels[1] = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should control to draw statusline, vsplitter
|
|
|
|
if editor.config.Editor.DrawWindowSeparator && win.grid == 1 {
|
|
|
|
|
|
|
|
isSkipDraw := true
|
|
|
|
if win.s.name != "minimap" {
|
|
|
|
|
|
|
|
// Draw bottom statusline
|
|
|
|
if row == win.rows-2 {
|
|
|
|
isSkipDraw = false
|
|
|
|
}
|
|
|
|
// Draw tabline
|
|
|
|
if row == 0 {
|
|
|
|
isSkipDraw = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// // Do not Draw statusline of splitted window
|
|
|
|
// win.s.windows.Range(func(_, winITF interface{}) bool {
|
|
|
|
// w := winITF.(*Window)
|
|
|
|
// if w == nil {
|
|
|
|
// return true
|
|
|
|
// }
|
|
|
|
// if !w.isShown() {
|
|
|
|
// return true
|
|
|
|
// }
|
|
|
|
// if row == w.pos[1]-1 {
|
|
|
|
// isDraw = true
|
|
|
|
// return false
|
|
|
|
// }
|
|
|
|
// return true
|
|
|
|
// })
|
|
|
|
} else {
|
|
|
|
isSkipDraw = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if isSkipDraw {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
win.updateLine(colStart, row, cells)
|
|
|
|
win.countContent(row)
|
|
|
|
win.makeUpdateMask(row)
|
2021-10-14 23:25:27 +09:00
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
if !win.isShown() {
|
|
|
|
win.show()
|
|
|
|
}
|
|
|
|
|
|
|
|
if win.isMsgGrid {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if win.grid == 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if win.maxLenContent < win.lenContent[row] {
|
|
|
|
win.maxLenContent = win.lenContent[row]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) updateLine(col, row int, cells []interface{}) {
|
|
|
|
w.updateMutex.Lock()
|
|
|
|
line := w.content[row]
|
|
|
|
colStart := col
|
|
|
|
for _, arg := range cells {
|
|
|
|
if col >= len(line) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
cell := arg.([]interface{})
|
|
|
|
|
|
|
|
var hl, repeat int
|
|
|
|
hl = -1
|
|
|
|
if len(cell) >= 2 {
|
|
|
|
hl = util.ReflectToInt(cell[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(cell) == 3 {
|
|
|
|
repeat = util.ReflectToInt(cell[2])
|
|
|
|
}
|
|
|
|
|
|
|
|
// If `repeat` is present, the cell should be
|
|
|
|
// repeated `repeat` times (including the first time), otherwise just
|
|
|
|
// once.
|
|
|
|
r := 1
|
|
|
|
if repeat == 0 {
|
|
|
|
repeat = 1
|
|
|
|
}
|
|
|
|
for r <= repeat {
|
|
|
|
if col >= len(line) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if line[col] == nil {
|
|
|
|
line[col] = &Cell{}
|
|
|
|
}
|
|
|
|
|
2021-10-14 23:25:27 +09:00
|
|
|
line[col].char = cell[0].(string)
|
2021-05-02 18:08:37 +09:00
|
|
|
line[col].normalWidth = w.isNormalWidth(line[col].char)
|
|
|
|
|
|
|
|
// If `hl_id` is not present the most recently seen `hl_id` in
|
|
|
|
// the same call should be used (it is always sent for the first
|
|
|
|
// cell in the event).
|
2021-10-08 23:49:45 +09:00
|
|
|
var hltmp *Highlight
|
2021-05-02 18:08:37 +09:00
|
|
|
switch col {
|
|
|
|
case 0:
|
2021-10-08 23:49:45 +09:00
|
|
|
hltmp = w.s.hlAttrDef[hl]
|
2021-05-02 18:08:37 +09:00
|
|
|
default:
|
|
|
|
if hl == -1 {
|
2021-10-08 23:49:45 +09:00
|
|
|
hltmp = line[col-1].highlight
|
2021-05-02 18:08:37 +09:00
|
|
|
} else {
|
2021-10-08 23:49:45 +09:00
|
|
|
hltmp = w.s.hlAttrDef[hl]
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-08 23:49:45 +09:00
|
|
|
if line[col].highlight != nil {
|
|
|
|
line[col].isUpdateBg = !hltmp.bg().equals(line[col].highlight.bg())
|
|
|
|
}
|
|
|
|
line[col].highlight = hltmp
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
// Detect popupmenu
|
|
|
|
if line[col].highlight.uiName == "Pmenu" ||
|
|
|
|
line[col].highlight.uiName == "PmenuSel" ||
|
|
|
|
line[col].highlight.uiName == "PmenuSbar" {
|
|
|
|
w.isPopupmenu = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect winblend
|
|
|
|
if line[col].highlight.blend > 0 {
|
|
|
|
w.wb = line[col].highlight.blend
|
|
|
|
}
|
|
|
|
|
|
|
|
col++
|
|
|
|
r++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.updateMutex.Unlock()
|
|
|
|
|
|
|
|
w.queueRedraw(colStart, row, col-colStart+1, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) countContent(row int) {
|
|
|
|
line := w.content[row]
|
|
|
|
lenLine := w.cols - 1
|
|
|
|
width := w.cols - 1
|
|
|
|
var breakFlag [2]bool
|
|
|
|
for j := w.cols - 1; j >= 0; j-- {
|
|
|
|
cell := line[j]
|
|
|
|
|
|
|
|
if !breakFlag[0] {
|
|
|
|
if cell == nil {
|
|
|
|
lenLine--
|
|
|
|
} else if cell.char == " " {
|
|
|
|
lenLine--
|
|
|
|
} else {
|
|
|
|
breakFlag[0] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !breakFlag[1] {
|
|
|
|
if cell == nil {
|
|
|
|
width--
|
2021-10-14 23:25:27 +09:00
|
|
|
} else if cell.char == " " && !cell.isUpdateBg {
|
2021-05-02 18:08:37 +09:00
|
|
|
width--
|
|
|
|
} else {
|
|
|
|
breakFlag[1] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if breakFlag[0] && breakFlag[1] {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lenLine++
|
|
|
|
width++
|
|
|
|
|
|
|
|
w.lenLine[row] = lenLine
|
|
|
|
w.lenContent[row] = width
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) makeUpdateMask(row int) {
|
2021-10-14 23:25:27 +09:00
|
|
|
for j, cell := range w.content[row] {
|
2021-05-02 18:08:37 +09:00
|
|
|
if cell == nil {
|
|
|
|
w.contentMask[row][j] = true
|
2021-10-08 23:49:45 +09:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the target cell is blank and there is no text decoration of any kind
|
|
|
|
if cell.char == " " &&
|
|
|
|
!cell.highlight.underline &&
|
|
|
|
!cell.highlight.undercurl &&
|
|
|
|
!cell.highlight.strikethrough {
|
|
|
|
|
|
|
|
// If the background color has not been updated
|
|
|
|
if !cell.isUpdateBg {
|
|
|
|
w.contentMask[row][j] = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the target cell is outside the range of neovim's update event,
|
|
|
|
// it will not be drawn.
|
|
|
|
if j < w.queueRedrawArea[0] || j > w.queueRedrawArea[2] {
|
|
|
|
w.contentMask[row][j] = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
w.contentMask[row][j] = true
|
2021-05-02 18:08:37 +09:00
|
|
|
} else {
|
|
|
|
w.contentMask[row][j] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) countHeadSpaceOfLine(y int) (int, error) {
|
|
|
|
if w == nil {
|
|
|
|
return 0, errors.New("window is nil")
|
|
|
|
}
|
|
|
|
if y >= len(w.content) || w.content == nil {
|
|
|
|
return 0, errors.New("content is nil")
|
|
|
|
}
|
|
|
|
line := w.content[y]
|
|
|
|
count := 0
|
|
|
|
for _, c := range line {
|
|
|
|
if c == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.char != " " && !c.isSignColumn() {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cell) isSignColumn() bool {
|
|
|
|
switch c.highlight.hlName {
|
|
|
|
case "SignColumn",
|
|
|
|
"FoldColumn",
|
|
|
|
"LineNr",
|
|
|
|
"CursorLineNr",
|
|
|
|
"ALEErrorSign",
|
|
|
|
"ALEStyleErrorSign",
|
|
|
|
"ALEWarningSign",
|
|
|
|
"ALEStyleWarningSign",
|
|
|
|
"ALEInfoSign",
|
|
|
|
"ALESignColumnWithErrors",
|
|
|
|
"LspErrorHighlight",
|
|
|
|
"LspWarningHighlight",
|
|
|
|
"LspInformationHighlight",
|
|
|
|
"LspHintHighlight":
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) scroll(count int) {
|
2021-09-04 20:44:04 +09:00
|
|
|
top := w.scrollRegion[0]
|
|
|
|
bot := w.scrollRegion[1]
|
|
|
|
left := w.scrollRegion[2]
|
|
|
|
right := w.scrollRegion[3]
|
|
|
|
|
|
|
|
// If the rectangular area to be scrolled matches
|
|
|
|
// the entire area of the grid, we simply shift the content slice.
|
|
|
|
if top == 0 && bot == w.rows-1 {
|
|
|
|
c := count
|
|
|
|
if count < 0 {
|
|
|
|
c = c * -1
|
|
|
|
}
|
2021-08-18 01:34:16 +09:00
|
|
|
|
2021-09-04 20:44:04 +09:00
|
|
|
content := make([][]*Cell, c)
|
|
|
|
contentMask := make([][]bool, c)
|
|
|
|
lenLine := make([]int, c)
|
|
|
|
lenContent := make([]int, c)
|
2021-08-25 00:02:31 +09:00
|
|
|
|
2021-09-04 20:44:04 +09:00
|
|
|
for i := 0; i < c; i++ {
|
|
|
|
content[i] = make([]*Cell, w.cols)
|
|
|
|
contentMask[i] = make([]bool, w.cols)
|
|
|
|
}
|
2021-08-25 00:02:31 +09:00
|
|
|
|
2021-09-04 20:44:04 +09:00
|
|
|
if count > 0 {
|
|
|
|
w.content = w.content[count:]
|
|
|
|
w.content = append(w.content, content...)
|
|
|
|
w.contentMask = w.contentMask[count:]
|
|
|
|
w.contentMask = append(w.contentMask, contentMask...)
|
2021-08-25 00:02:31 +09:00
|
|
|
|
2021-09-04 20:44:04 +09:00
|
|
|
w.lenLine = w.lenLine[count:]
|
|
|
|
w.lenLine = append(w.lenLine, lenLine...)
|
|
|
|
w.lenContent = w.lenContent[count:]
|
|
|
|
w.lenContent = append(w.lenContent, lenContent...)
|
|
|
|
}
|
|
|
|
if count < 0 {
|
|
|
|
w.content = w.content[:w.rows+count]
|
|
|
|
w.content = append(content, w.content...)
|
|
|
|
w.contentMask = w.contentMask[:w.rows+count]
|
|
|
|
w.contentMask = append(contentMask, w.contentMask...)
|
|
|
|
|
|
|
|
w.lenLine = w.lenLine[:w.rows+count]
|
|
|
|
w.lenLine = append(lenLine, w.lenLine...)
|
|
|
|
w.lenContent = w.lenContent[:w.rows+count]
|
|
|
|
w.lenContent = append(lenContent, w.lenContent...)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If the rectangular area to be scrolled does not match
|
|
|
|
// the entire area of the grid
|
|
|
|
|
|
|
|
if count > 0 {
|
|
|
|
for row := top; row <= bot-count; row++ {
|
|
|
|
w.scrollContentByCount(row, left, right, bot, count)
|
|
|
|
}
|
|
|
|
for row := bot - count + 1; row <= bot; row++ {
|
|
|
|
w.clearLinesWhereContentHasPassed(row, left, right)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if count < 0 {
|
|
|
|
for row := bot; row >= top-count; row-- {
|
|
|
|
w.scrollContentByCount(row, left, right, bot, count)
|
|
|
|
}
|
|
|
|
for row := top - count - 1; row >= top; row-- {
|
|
|
|
w.clearLinesWhereContentHasPassed(row, left, right)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
// Suppresses flickering during smooth scrolling
|
|
|
|
if w.scrollPixels[1] != 0 {
|
|
|
|
w.scrollPixels[1] = 0
|
|
|
|
}
|
|
|
|
|
2021-08-25 00:02:31 +09:00
|
|
|
// w.queueRedraw(left, top, (right - left + 1), (bot - top + 1))
|
2021-09-04 20:44:04 +09:00
|
|
|
w.queueRedraw(0, top, w.cols, bot-top+1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// scrollContentByCount a function to shift the contents of w.content array by count.
|
|
|
|
func (w *Window) scrollContentByCount(row, left, right, bot, count int) {
|
|
|
|
if len(w.content) <= bot {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy(w.content[row], w.content[row+count])
|
|
|
|
// copy(w.contentMask[row], w.contentMask[row+count])
|
|
|
|
for col := left; col <= right; col++ {
|
|
|
|
w.content[row][col] = w.content[row+count][col]
|
|
|
|
w.contentMask[row][col] = w.contentMask[row+count][col]
|
|
|
|
}
|
|
|
|
w.lenLine[row] = w.lenLine[row+count]
|
|
|
|
w.lenContent[row] = w.lenContent[row+count]
|
|
|
|
}
|
|
|
|
|
|
|
|
// clearLinesWhereContentHasPassed is a function to clear the source area
|
|
|
|
// after shifting the contents of w.content array by count.
|
|
|
|
func (w *Window) clearLinesWhereContentHasPassed(row, left, right int) {
|
|
|
|
for col := left; col <= right; col++ {
|
|
|
|
w.content[row][col] = nil
|
|
|
|
w.contentMask[row][col] = true
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) update() {
|
|
|
|
if w == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-20 23:05:44 +09:00
|
|
|
w.redrawMutex.Lock()
|
|
|
|
|
|
|
|
font := w.getFont()
|
2021-05-02 18:08:37 +09:00
|
|
|
start := w.queueRedrawArea[1]
|
|
|
|
end := w.queueRedrawArea[3]
|
2021-06-06 15:46:14 +09:00
|
|
|
// Update all lines when using the wheel scroll or indent guide feature.
|
|
|
|
if w.scrollPixels[1] != 0 || editor.config.Editor.IndentGuide {
|
2021-05-02 18:08:37 +09:00
|
|
|
start = 0
|
|
|
|
end = w.rows
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := start; i < end; i++ {
|
|
|
|
|
|
|
|
if len(w.content) <= i {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
width := w.lenContent[i]
|
|
|
|
|
|
|
|
if width < w.lenOldContent[i] {
|
|
|
|
width = w.lenOldContent[i]
|
|
|
|
}
|
|
|
|
w.lenOldContent[i] = w.lenContent[i]
|
|
|
|
|
|
|
|
drawWithSingleRect := false
|
|
|
|
|
|
|
|
// If DrawIndentGuide is enabled
|
|
|
|
if editor.config.Editor.IndentGuide {
|
|
|
|
if i < w.rows-1 {
|
|
|
|
if width < w.lenContent[i+1] {
|
|
|
|
width = w.lenContent[i+1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drawWithSingleRect = true
|
|
|
|
}
|
|
|
|
// If screen is minimap
|
|
|
|
if w.s.name == "minimap" {
|
|
|
|
width = w.cols
|
|
|
|
drawWithSingleRect = true
|
|
|
|
}
|
|
|
|
// If scroll is smooth with touchpad
|
|
|
|
if w.scrollPixels[1] != 0 {
|
|
|
|
width = w.maxLenContent
|
|
|
|
drawWithSingleRect = true
|
|
|
|
}
|
|
|
|
// If scroll is smooth
|
|
|
|
if editor.config.Editor.SmoothScroll {
|
|
|
|
if w.scrollPixels2 != 0 {
|
|
|
|
width = w.maxLenContent
|
|
|
|
drawWithSingleRect = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
width++
|
|
|
|
|
|
|
|
// Create rectangles that require updating.
|
|
|
|
var rects [][4]int
|
|
|
|
isCreateRect := false
|
|
|
|
start := 0
|
|
|
|
if drawWithSingleRect {
|
|
|
|
rect := [4]int{
|
|
|
|
0,
|
|
|
|
i * font.lineHeight,
|
|
|
|
int(math.Ceil(float64(width) * font.truewidth)),
|
|
|
|
font.lineHeight,
|
|
|
|
}
|
|
|
|
rects = append(rects, rect)
|
|
|
|
for j, _ := range w.contentMask[i] {
|
|
|
|
w.contentMaskOld[i][j] = w.contentMask[i][j]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for j, cm := range w.contentMask[i] {
|
|
|
|
mask := cm || w.contentMaskOld[i][j]
|
2021-08-25 23:14:47 +09:00
|
|
|
// Starting point for creating a rectangular area
|
2021-05-02 18:08:37 +09:00
|
|
|
if mask && !isCreateRect {
|
|
|
|
start = j
|
|
|
|
isCreateRect = true
|
|
|
|
}
|
2021-08-25 23:14:47 +09:00
|
|
|
// Judgment point for end of rectangular area creation
|
2021-05-02 18:08:37 +09:00
|
|
|
if (!mask && isCreateRect) || (j >= len(w.contentMask[i])-1 && isCreateRect) {
|
2021-10-08 23:49:45 +09:00
|
|
|
// If the next rectangular area will be created with only one cell separating it, merge it.
|
|
|
|
if j+1 <= len(w.contentMask[i])-1 {
|
|
|
|
if w.contentMask[i][j+1] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2021-08-25 23:14:47 +09:00
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
jj := j
|
2021-08-25 23:14:47 +09:00
|
|
|
|
|
|
|
// If it reaches the edge of the grid
|
2021-05-02 18:08:37 +09:00
|
|
|
if j >= len(w.contentMask[i])-1 && isCreateRect {
|
|
|
|
jj++
|
|
|
|
}
|
2021-08-25 23:14:47 +09:00
|
|
|
|
|
|
|
// create rectangular area
|
2021-05-02 18:08:37 +09:00
|
|
|
rect := [4]int{
|
|
|
|
int(float64(start) * font.truewidth),
|
|
|
|
i * font.lineHeight,
|
|
|
|
int(math.Ceil(float64(jj-start+1) * font.truewidth)),
|
|
|
|
font.lineHeight,
|
|
|
|
}
|
|
|
|
rects = append(rects, rect)
|
|
|
|
isCreateRect = false
|
|
|
|
}
|
|
|
|
w.contentMaskOld[i][j] = w.contentMask[i][j]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request screen refresh for each rectangle region.
|
|
|
|
if len(rects) == 0 {
|
|
|
|
w.Update2(
|
|
|
|
0,
|
|
|
|
i*font.lineHeight,
|
|
|
|
int(float64(width)*font.truewidth),
|
|
|
|
font.lineHeight,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
for _, rect := range rects {
|
|
|
|
w.Update2(
|
|
|
|
rect[0],
|
|
|
|
rect[1],
|
|
|
|
rect[2],
|
|
|
|
rect[3],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset redraw area
|
|
|
|
w.queueRedrawArea[0] = w.cols
|
|
|
|
w.queueRedrawArea[1] = w.rows
|
|
|
|
w.queueRedrawArea[2] = 0
|
|
|
|
w.queueRedrawArea[3] = 0
|
|
|
|
|
|
|
|
w.redrawMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) queueRedrawAll() {
|
|
|
|
w.queueRedrawArea = [4]int{0, 0, w.cols, w.rows}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) queueRedraw(x, y, width, height int) {
|
|
|
|
w.redrawMutex.Lock()
|
|
|
|
if x < w.queueRedrawArea[0] {
|
|
|
|
w.queueRedrawArea[0] = x
|
|
|
|
}
|
|
|
|
if y < w.queueRedrawArea[1] {
|
|
|
|
w.queueRedrawArea[1] = y
|
|
|
|
}
|
|
|
|
if (x + width) > w.queueRedrawArea[2] {
|
|
|
|
w.queueRedrawArea[2] = x + width
|
|
|
|
}
|
|
|
|
if (y + height) > w.queueRedrawArea[3] {
|
|
|
|
w.queueRedrawArea[3] = y + height
|
|
|
|
}
|
|
|
|
w.redrawMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawBackground(p *gui.QPainter, y int, col int, cols int) {
|
|
|
|
if y >= len(w.content) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
font := w.getFont()
|
|
|
|
line := w.content[y]
|
|
|
|
var bg *RGBA
|
|
|
|
|
2021-09-26 21:53:52 +09:00
|
|
|
// fmt.Println("win.grid", w.grid, "float", w.isFloatWin, "popup", w.isPopupmenu)
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
// draw default background color if window is float window or msg grid
|
|
|
|
isDrawDefaultBg := false
|
2021-09-25 15:23:31 +09:00
|
|
|
if w.isFloatWin || w.isMsgGrid {
|
2021-09-26 21:53:52 +09:00
|
|
|
// If transparent is true, then we should draw every cell's background color
|
2021-10-09 01:19:56 +09:00
|
|
|
if editor.config.Editor.Transparent < 1.0 || editor.config.Message.Transparent < 1.0 {
|
2021-09-26 21:53:52 +09:00
|
|
|
isDrawDefaultBg = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the window is popupmenu and pumblend is set
|
|
|
|
if w.isPopupmenu {
|
|
|
|
if editor.config.Editor.Transparent < 1.0 {
|
|
|
|
w.SetAutoFillBackground(false)
|
|
|
|
}
|
|
|
|
if w.s.ws.pb > 0 {
|
|
|
|
w.SetAutoFillBackground(false)
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
2021-09-26 21:53:52 +09:00
|
|
|
// If the window is float window and winblend is set
|
|
|
|
if w.isFloatWin && !w.isPopupmenu {
|
|
|
|
if editor.config.Editor.Transparent < 1.0 {
|
|
|
|
w.SetAutoFillBackground(false)
|
|
|
|
}
|
|
|
|
if w.wb > 0 {
|
|
|
|
w.SetAutoFillBackground(false)
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-22 16:00:02 +09:00
|
|
|
// Set smooth scroll offset
|
2021-05-23 01:19:05 +09:00
|
|
|
scrollPixels := 0
|
|
|
|
if w.lastScrollphase != core.Qt__NoScrollPhase {
|
|
|
|
scrollPixels = w.scrollPixels2
|
|
|
|
}
|
2021-05-22 16:00:02 +09:00
|
|
|
if editor.config.Editor.LineToScroll == 1 {
|
|
|
|
scrollPixels += w.scrollPixels[1]
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
// isDrawDefaultBg := true
|
|
|
|
// // Simply paint the color into a rectangle
|
|
|
|
// for x := col; x <= col+cols; x++ {
|
|
|
|
// if x >= len(line) {
|
|
|
|
// continue
|
|
|
|
// }
|
|
|
|
// var highlight *Highlight
|
|
|
|
// if line[x] == nil {
|
|
|
|
// highlight = w.s.hlAttrDef[0]
|
|
|
|
// } else {
|
|
|
|
// highlight = line[x].highlight
|
|
|
|
// }
|
|
|
|
// if !bg.equals(w.s.ws.background) || isDrawDefaultBg {
|
|
|
|
// // Set diff pattern
|
|
|
|
// pattern, color, transparent := w.getFillpatternAndTransparent(highlight)
|
|
|
|
// // Fill background with pattern
|
|
|
|
// rectF := core.NewQRectF4(
|
|
|
|
// float64(x)*font.truewidth,
|
|
|
|
// float64((y)*font.lineHeight),
|
|
|
|
// font.truewidth,
|
|
|
|
// float64(font.lineHeight),
|
|
|
|
// )
|
|
|
|
// p.FillRect(
|
|
|
|
// rectF,
|
|
|
|
// gui.NewQBrush3(
|
|
|
|
// gui.NewQColor3(
|
|
|
|
// color.R,
|
|
|
|
// color.G,
|
|
|
|
// color.B,
|
|
|
|
// transparent,
|
|
|
|
// ),
|
|
|
|
// pattern,
|
|
|
|
// ),
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// The same color combines the rectangular areas and paints at once
|
|
|
|
var start, end, width int
|
|
|
|
var lastBg *RGBA
|
|
|
|
var lastHighlight, highlight *Highlight
|
|
|
|
|
2021-08-23 22:33:30 +09:00
|
|
|
fillCellRect := func() {
|
|
|
|
if lastHighlight == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
width = end - start + 1
|
|
|
|
if width < 0 {
|
|
|
|
width = 0
|
|
|
|
}
|
|
|
|
if !isDrawDefaultBg && lastBg.equals(w.background) {
|
|
|
|
width = 0
|
|
|
|
}
|
|
|
|
if width > 0 {
|
|
|
|
// Set diff pattern
|
|
|
|
pattern, color, transparent := w.getFillpatternAndTransparent(lastHighlight)
|
|
|
|
|
|
|
|
// Fill background with pattern
|
|
|
|
rectF := core.NewQRectF4(
|
|
|
|
float64(start)*font.truewidth,
|
|
|
|
float64((y)*font.lineHeight+scrollPixels),
|
|
|
|
float64(width)*font.truewidth,
|
|
|
|
float64(font.lineHeight),
|
|
|
|
)
|
|
|
|
p.FillRect(
|
|
|
|
rectF,
|
|
|
|
gui.NewQBrush3(
|
|
|
|
gui.NewQColor3(
|
|
|
|
color.R,
|
|
|
|
color.G,
|
|
|
|
color.B,
|
|
|
|
transparent,
|
2021-05-02 18:08:37 +09:00
|
|
|
),
|
2021-08-23 22:33:30 +09:00
|
|
|
pattern,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
width = 0
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
2021-08-23 22:33:30 +09:00
|
|
|
}
|
|
|
|
for x := col; x <= col+cols; x++ {
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
if x >= len(line)+1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if x < len(line) {
|
|
|
|
if line[x] == nil {
|
|
|
|
highlight = w.s.hlAttrDef[0]
|
|
|
|
} else {
|
|
|
|
highlight = line[x].highlight
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
highlight = w.s.hlAttrDef[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
bg = highlight.bg()
|
|
|
|
|
|
|
|
bounds := col + cols
|
|
|
|
if col+cols > len(line) {
|
|
|
|
bounds = len(line)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lastBg == nil {
|
|
|
|
start = x
|
|
|
|
end = x
|
|
|
|
lastBg = bg
|
|
|
|
lastHighlight = highlight
|
|
|
|
}
|
|
|
|
if lastBg != nil {
|
|
|
|
if lastBg.equals(bg) {
|
|
|
|
end = x
|
|
|
|
}
|
|
|
|
if !lastBg.equals(bg) || x == bounds {
|
|
|
|
fillCellRect()
|
|
|
|
|
|
|
|
start = x
|
|
|
|
end = x
|
|
|
|
lastBg = bg
|
|
|
|
lastHighlight = highlight
|
|
|
|
|
|
|
|
if x == bounds {
|
|
|
|
fillCellRect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawText(p *gui.QPainter, y int, col int, cols int) {
|
|
|
|
if y >= len(w.content) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
wsfont := w.getFont()
|
|
|
|
|
|
|
|
if !editor.config.Editor.CachedDrawing {
|
|
|
|
p.SetFont(wsfont.fontNew)
|
|
|
|
}
|
|
|
|
|
|
|
|
line := w.content[y]
|
|
|
|
chars := map[*Highlight][]int{}
|
|
|
|
specialChars := []int{}
|
|
|
|
|
2021-05-22 16:00:02 +09:00
|
|
|
// Set smooth scroll offset
|
2021-05-23 01:19:05 +09:00
|
|
|
scrollPixels := 0
|
|
|
|
if w.lastScrollphase != core.Qt__NoScrollPhase {
|
|
|
|
scrollPixels = w.scrollPixels2
|
|
|
|
}
|
2021-05-22 16:00:02 +09:00
|
|
|
if editor.config.Editor.LineToScroll == 1 {
|
|
|
|
scrollPixels += w.scrollPixels[1]
|
|
|
|
}
|
|
|
|
|
2021-10-08 23:49:45 +09:00
|
|
|
// pointX := float64(col) * wsfont.truewidth
|
2021-05-02 18:08:37 +09:00
|
|
|
for x := col; x <= col+cols; x++ {
|
|
|
|
if x >= len(line) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if line[x] == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if line[x].char == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if line[x].char == " " {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !line[x].normalWidth {
|
|
|
|
specialChars = append(specialChars, x)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the ligature setting is disabled,
|
|
|
|
// we will draw the characters on the screen one by one.
|
|
|
|
if editor.config.Editor.DisableLigatures {
|
|
|
|
|
|
|
|
// if CachedDrawing is disabled
|
|
|
|
if !editor.config.Editor.CachedDrawing {
|
|
|
|
w.drawTextInPos(
|
|
|
|
p,
|
2021-10-01 20:47:08 +09:00
|
|
|
// core.NewQPointF3(
|
|
|
|
// float64(x)*wsfont.truewidth,
|
|
|
|
// float64(y*wsfont.lineHeight+wsfont.shift+scrollPixels),
|
|
|
|
// ),
|
|
|
|
int(float64(x)*wsfont.truewidth),
|
|
|
|
y*wsfont.lineHeight+wsfont.shift+scrollPixels,
|
2021-05-02 18:08:37 +09:00
|
|
|
line[x].char,
|
|
|
|
line[x].highlight,
|
|
|
|
true,
|
|
|
|
)
|
|
|
|
|
2021-05-18 21:29:21 +09:00
|
|
|
// if CachedDrawing is enabled
|
2021-05-02 18:08:37 +09:00
|
|
|
} else {
|
|
|
|
w.drawTextInPosWithCache(
|
|
|
|
p,
|
2021-10-01 20:47:08 +09:00
|
|
|
// core.NewQPointF3(
|
|
|
|
// float64(x)*wsfont.truewidth,
|
|
|
|
// float64(y*wsfont.lineHeight+scrollPixels),
|
|
|
|
// ),
|
|
|
|
int(float64(x)*wsfont.truewidth),
|
|
|
|
y*wsfont.lineHeight+scrollPixels,
|
2021-05-02 18:08:37 +09:00
|
|
|
line[x].char,
|
|
|
|
line[x].highlight,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Prepare to draw a group of identical highlight units.
|
|
|
|
highlight := line[x].highlight
|
|
|
|
colorSlice, ok := chars[highlight]
|
|
|
|
if !ok {
|
|
|
|
colorSlice = []int{}
|
|
|
|
}
|
|
|
|
colorSlice = append(colorSlice, x)
|
|
|
|
chars[highlight] = colorSlice
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the normal rendering process for goneovim,
|
|
|
|
// we draw a word snippet of the same highlight on the screen for each of the highlights.
|
|
|
|
if !editor.config.Editor.DisableLigatures {
|
|
|
|
|
2021-10-08 23:49:45 +09:00
|
|
|
// // var pointf *core.QPointF
|
|
|
|
// var X, Y int
|
|
|
|
// // if CachedDrawing is disabled
|
|
|
|
// if !editor.config.Editor.CachedDrawing {
|
|
|
|
// // pointf = core.NewQPointF3(
|
|
|
|
// // pointX,
|
|
|
|
// // float64((y)*wsfont.lineHeight+wsfont.shift+scrollPixels),
|
|
|
|
// // )
|
|
|
|
// X = int(pointX)
|
|
|
|
// Y = int(float64((y)*wsfont.lineHeight+wsfont.shift+scrollPixels))
|
|
|
|
// } else { // if CachedDrawing is enabled
|
|
|
|
// // pointf = core.NewQPointF3(
|
|
|
|
// // pointX,
|
|
|
|
// // float64(y*wsfont.lineHeight+scrollPixels),
|
|
|
|
// // )
|
|
|
|
// X = int(pointX)
|
|
|
|
// Y = int(float64(y*wsfont.lineHeight+scrollPixels))
|
|
|
|
// }
|
2021-05-16 22:16:02 +09:00
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
for highlight, colorSlice := range chars {
|
|
|
|
var buffer bytes.Buffer
|
2021-08-20 23:05:44 +09:00
|
|
|
slice := colorSlice
|
2021-05-02 18:08:37 +09:00
|
|
|
|
2021-10-08 23:49:45 +09:00
|
|
|
pos := col
|
|
|
|
isIndentationWhitespace := true
|
2021-05-02 18:08:37 +09:00
|
|
|
for x := col; x <= col+cols; x++ {
|
|
|
|
if len(slice) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
index := slice[0]
|
|
|
|
|
|
|
|
if x < index {
|
2021-10-08 23:49:45 +09:00
|
|
|
// buffer.WriteString(" ")
|
|
|
|
if isIndentationWhitespace {
|
|
|
|
pos++
|
|
|
|
} else {
|
|
|
|
buffer.WriteString(" ")
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if x == index {
|
|
|
|
buffer.WriteString(line[x].char)
|
|
|
|
slice = slice[1:]
|
2021-10-08 23:49:45 +09:00
|
|
|
isIndentationWhitespace = false
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
text := buffer.String()
|
|
|
|
|
|
|
|
// if CachedDrawing is disabled
|
|
|
|
if !editor.config.Editor.CachedDrawing {
|
|
|
|
w.drawTextInPos(
|
|
|
|
p,
|
2021-10-01 20:47:08 +09:00
|
|
|
// pointf,
|
2021-10-08 23:49:45 +09:00
|
|
|
int(float64(pos)*wsfont.truewidth),
|
|
|
|
y*wsfont.lineHeight+wsfont.shift+scrollPixels,
|
2021-05-02 18:08:37 +09:00
|
|
|
text,
|
|
|
|
highlight,
|
|
|
|
true,
|
|
|
|
)
|
2021-05-18 21:29:21 +09:00
|
|
|
} else { // if CachedDrawing is enabled
|
2021-05-02 18:08:37 +09:00
|
|
|
w.drawTextInPosWithCache(
|
|
|
|
p,
|
2021-10-01 20:47:08 +09:00
|
|
|
// pointf,
|
2021-10-08 23:49:45 +09:00
|
|
|
int(float64(pos)*wsfont.truewidth),
|
|
|
|
y*wsfont.lineHeight+scrollPixels,
|
2021-05-02 18:08:37 +09:00
|
|
|
text,
|
|
|
|
highlight,
|
|
|
|
true,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(specialChars) >= 1 {
|
|
|
|
if !editor.config.Editor.CachedDrawing {
|
|
|
|
if w.s.ws.fontwide != nil && w.font == nil && w.s.ws.fontwide.fontNew != nil {
|
|
|
|
p.SetFont(w.s.ws.fontwide.fontNew)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, x := range specialChars {
|
|
|
|
if line[x] == nil || line[x].char == " " {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// if CachedDrawing is disabled
|
|
|
|
if !editor.config.Editor.CachedDrawing {
|
|
|
|
w.drawTextInPos(
|
2021-05-18 21:29:21 +09:00
|
|
|
p,
|
2021-10-01 20:47:08 +09:00
|
|
|
// core.NewQPointF3(
|
|
|
|
// float64(x)*wsfont.truewidth,
|
|
|
|
// float64(y*wsfont.lineHeight+wsfont.shift+scrollPixels),
|
|
|
|
// ),
|
|
|
|
int(float64(x)*wsfont.truewidth),
|
|
|
|
y*wsfont.lineHeight+wsfont.shift+scrollPixels,
|
2021-05-02 18:08:37 +09:00
|
|
|
line[x].char,
|
|
|
|
line[x].highlight,
|
|
|
|
false,
|
|
|
|
)
|
2021-05-18 21:29:21 +09:00
|
|
|
} else { // if CachedDrawing is enabled
|
2021-05-02 18:08:37 +09:00
|
|
|
w.drawTextInPosWithCache(
|
|
|
|
p,
|
2021-10-01 20:47:08 +09:00
|
|
|
// core.NewQPointF3(
|
|
|
|
// float64(x)*wsfont.truewidth,
|
|
|
|
// float64(y*wsfont.lineHeight+scrollPixels),
|
|
|
|
// ),
|
|
|
|
int(float64(x)*wsfont.truewidth),
|
|
|
|
y*wsfont.lineHeight+scrollPixels,
|
2021-05-02 18:08:37 +09:00
|
|
|
line[x].char,
|
|
|
|
line[x].highlight,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 20:47:08 +09:00
|
|
|
func (w *Window) drawTextInPosWithCache(p *gui.QPainter, x, y int, text string, highlight *Highlight, isNormalWidth bool) {
|
2021-05-02 18:08:37 +09:00
|
|
|
if text == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fgCache := w.getCache()
|
|
|
|
var image *gui.QImage
|
|
|
|
imagev, err := fgCache.get(HlChars{
|
|
|
|
text: text,
|
|
|
|
fg: highlight.fg(),
|
|
|
|
italic: highlight.italic,
|
|
|
|
bold: highlight.bold,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
image = w.newTextCache(text, highlight, isNormalWidth)
|
|
|
|
w.setTextCache(text, highlight, image)
|
|
|
|
} else {
|
|
|
|
image = imagev.(*gui.QImage)
|
|
|
|
}
|
|
|
|
|
2021-10-01 20:47:08 +09:00
|
|
|
// p.DrawImage7(
|
|
|
|
// point,
|
|
|
|
// image,
|
|
|
|
// )
|
2021-10-08 23:49:45 +09:00
|
|
|
p.DrawImage9(
|
2021-10-01 20:47:08 +09:00
|
|
|
x, y,
|
2021-05-02 18:08:37 +09:00
|
|
|
image,
|
2021-10-01 20:47:08 +09:00
|
|
|
0, 0,
|
|
|
|
-1, -1,
|
|
|
|
core.Qt__AutoColor,
|
2021-05-02 18:08:37 +09:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) setDecorationCache(highlight *Highlight, image *gui.QImage) {
|
|
|
|
if w.font != nil {
|
|
|
|
// If window has own font setting
|
|
|
|
w.fgCache.set(
|
|
|
|
HlDecoration{
|
|
|
|
fg: highlight.fg(),
|
|
|
|
underline: highlight.underline,
|
|
|
|
undercurl: highlight.undercurl,
|
|
|
|
strikethrough: highlight.strikethrough,
|
|
|
|
},
|
|
|
|
image,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// screen text cache
|
|
|
|
w.s.fgCache.set(
|
|
|
|
HlDecoration{
|
|
|
|
fg: highlight.fg(),
|
|
|
|
underline: highlight.underline,
|
|
|
|
undercurl: highlight.undercurl,
|
|
|
|
strikethrough: highlight.strikethrough,
|
|
|
|
},
|
|
|
|
image,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) newDecorationCache(char string, highlight *Highlight, isNormalWidth bool) *gui.QImage {
|
|
|
|
font := w.getFont()
|
|
|
|
|
|
|
|
width := font.truewidth
|
|
|
|
fg := highlight.fg()
|
|
|
|
if !isNormalWidth {
|
|
|
|
width = math.Ceil(w.s.runeTextWidth(font, char))
|
|
|
|
}
|
|
|
|
|
2021-05-22 16:00:02 +09:00
|
|
|
// Set smooth scroll offset
|
2021-05-23 01:19:05 +09:00
|
|
|
scrollPixels := 0
|
|
|
|
if w.lastScrollphase != core.Qt__NoScrollPhase {
|
|
|
|
scrollPixels = w.scrollPixels2
|
|
|
|
}
|
2021-05-22 16:00:02 +09:00
|
|
|
if editor.config.Editor.LineToScroll == 1 {
|
|
|
|
scrollPixels += w.scrollPixels[1]
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
// QImage default device pixel ratio is 1.0,
|
|
|
|
// So we set the correct device pixel ratio
|
|
|
|
image := gui.NewQImage2(
|
|
|
|
core.NewQRectF4(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
w.devicePixelRatio*width,
|
|
|
|
w.devicePixelRatio*float64(font.lineHeight),
|
|
|
|
).Size().ToSize(),
|
|
|
|
gui.QImage__Format_ARGB32_Premultiplied,
|
|
|
|
)
|
|
|
|
image.SetDevicePixelRatio(w.devicePixelRatio)
|
|
|
|
image.Fill3(core.Qt__transparent)
|
|
|
|
|
|
|
|
pi := gui.NewQPainter2(image)
|
|
|
|
pi.SetPen2(fg.QColor())
|
|
|
|
|
|
|
|
pen := gui.NewQPen()
|
|
|
|
var color *gui.QColor
|
|
|
|
sp := highlight.special
|
|
|
|
if sp != nil {
|
|
|
|
color = sp.QColor()
|
|
|
|
pen.SetColor(color)
|
|
|
|
} else {
|
|
|
|
fg := highlight.foreground
|
|
|
|
color = fg.QColor()
|
|
|
|
pen.SetColor(color)
|
|
|
|
}
|
|
|
|
pi.SetPen(pen)
|
|
|
|
start := float64(0) * font.truewidth
|
|
|
|
end := float64(width) * font.truewidth
|
|
|
|
|
|
|
|
space := float64(font.lineSpace) / 3.0
|
|
|
|
if space > font.ascent/3.0 {
|
|
|
|
space = font.ascent / 3.0
|
|
|
|
}
|
|
|
|
descent := float64(font.height) - font.ascent
|
|
|
|
weight := int(math.Ceil(float64(font.height) / 16.0))
|
|
|
|
if weight < 1 {
|
|
|
|
weight = 1
|
|
|
|
}
|
|
|
|
if highlight.strikethrough {
|
2021-05-22 16:00:02 +09:00
|
|
|
Y := float64(0*font.lineHeight+scrollPixels) + float64(font.ascent)*0.65 + float64(font.lineSpace/2)
|
2021-05-02 18:08:37 +09:00
|
|
|
pi.FillRect5(
|
|
|
|
int(start),
|
|
|
|
int(Y),
|
|
|
|
int(math.Ceil(font.truewidth)),
|
|
|
|
weight,
|
|
|
|
color,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if highlight.underline {
|
|
|
|
pi.FillRect5(
|
|
|
|
int(start),
|
2021-05-22 16:00:02 +09:00
|
|
|
int(float64((0+1)*font.lineHeight+scrollPixels))-weight,
|
2021-05-02 18:08:37 +09:00
|
|
|
int(math.Ceil(font.truewidth)),
|
|
|
|
weight,
|
|
|
|
color,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if highlight.undercurl {
|
|
|
|
amplitude := descent*0.65 + float64(font.lineSpace)
|
|
|
|
maxAmplitude := font.ascent / 8.0
|
|
|
|
if amplitude >= maxAmplitude {
|
|
|
|
amplitude = maxAmplitude
|
|
|
|
}
|
|
|
|
freq := 1.0
|
|
|
|
phase := 0.0
|
2021-05-22 16:00:02 +09:00
|
|
|
Y := float64(0*font.lineHeight+scrollPixels) + float64(font.ascent+descent*0.3) + float64(font.lineSpace/2) + space
|
2021-05-02 18:08:37 +09:00
|
|
|
Y2 := Y + amplitude*math.Sin(0)
|
|
|
|
point := core.NewQPointF3(start, Y2)
|
|
|
|
path := gui.NewQPainterPath2(point)
|
|
|
|
for i := int(point.X()); i <= int(end); i++ {
|
|
|
|
Y2 = Y + amplitude*math.Sin(2*math.Pi*freq*float64(i)/font.truewidth+phase)
|
|
|
|
path.LineTo(core.NewQPointF3(float64(i), Y2))
|
|
|
|
}
|
|
|
|
pi.DrawPath(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
pi.DestroyQPainter()
|
|
|
|
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) setTextCache(text string, highlight *Highlight, image *gui.QImage) {
|
|
|
|
if w.font != nil {
|
|
|
|
// If window has own font setting
|
|
|
|
w.fgCache.set(
|
|
|
|
HlChars{
|
|
|
|
text: text,
|
|
|
|
fg: highlight.fg(),
|
|
|
|
italic: highlight.italic,
|
|
|
|
bold: highlight.bold,
|
|
|
|
},
|
|
|
|
image,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// screen text cache
|
|
|
|
w.s.fgCache.set(
|
|
|
|
HlChars{
|
|
|
|
text: text,
|
|
|
|
fg: highlight.fg(),
|
|
|
|
italic: highlight.italic,
|
|
|
|
bold: highlight.bold,
|
|
|
|
},
|
|
|
|
image,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) newTextCache(text string, highlight *Highlight, isNormalWidth bool) *gui.QImage {
|
|
|
|
// * Ref: https://stackoverflow.com/questions/40458515/a-best-way-to-draw-a-lot-of-independent-characters-in-qt5/40476430#40476430
|
|
|
|
editor.putLog("start creating word cache:", text)
|
|
|
|
|
|
|
|
font := w.getFont()
|
|
|
|
|
2021-06-26 16:47:15 +09:00
|
|
|
// Put debug log
|
|
|
|
if editor.opts.Debug != "" {
|
|
|
|
fi := gui.NewQFontInfo(font.fontNew)
|
|
|
|
editor.putLog(
|
|
|
|
"Outputs font information creating word cache:",
|
|
|
|
fi.Family(),
|
|
|
|
fi.PointSizeF(),
|
|
|
|
fi.StyleName(),
|
|
|
|
fmt.Sprintf("%v", fi.PointSizeF()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
width := float64(len(text)) * font.italicWidth
|
|
|
|
fg := highlight.fg()
|
|
|
|
if !isNormalWidth {
|
|
|
|
width = math.Ceil(w.s.runeTextWidth(font, text))
|
|
|
|
}
|
|
|
|
|
|
|
|
// QImage default device pixel ratio is 1.0,
|
|
|
|
// So we set the correct device pixel ratio
|
2021-05-16 00:18:35 +09:00
|
|
|
|
|
|
|
// image := gui.NewQImage2(
|
|
|
|
// core.NewQRectF4(
|
|
|
|
// 0,
|
|
|
|
// 0,
|
|
|
|
// w.devicePixelRatio*width,
|
|
|
|
// w.devicePixelRatio*float64(font.lineHeight),
|
|
|
|
// ).Size().ToSize(),
|
|
|
|
// gui.QImage__Format_ARGB32_Premultiplied,
|
|
|
|
// )
|
|
|
|
image := gui.NewQImage3(
|
|
|
|
int(w.devicePixelRatio*width),
|
|
|
|
int(w.devicePixelRatio*float64(font.lineHeight)),
|
2021-05-02 18:08:37 +09:00
|
|
|
gui.QImage__Format_ARGB32_Premultiplied,
|
|
|
|
)
|
|
|
|
image.SetDevicePixelRatio(w.devicePixelRatio)
|
|
|
|
image.Fill3(core.Qt__transparent)
|
|
|
|
|
|
|
|
pi := gui.NewQPainter2(image)
|
|
|
|
pi.SetPen2(fg.QColor())
|
|
|
|
|
|
|
|
if !isNormalWidth && w.font == nil && w.s.ws.fontwide != nil {
|
|
|
|
pi.SetFont(w.s.ws.fontwide.fontNew)
|
|
|
|
} else {
|
|
|
|
pi.SetFont(font.fontNew)
|
|
|
|
}
|
|
|
|
|
|
|
|
if highlight.bold {
|
|
|
|
// pi.Font().SetBold(true)
|
|
|
|
pi.Font().SetWeight(font.fontNew.Weight() + 25)
|
|
|
|
}
|
|
|
|
if highlight.italic {
|
|
|
|
pi.Font().SetItalic(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pi.DrawText6(
|
|
|
|
core.NewQRectF4(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
float64(font.lineHeight),
|
|
|
|
), text, gui.NewQTextOption2(core.Qt__AlignVCenter),
|
|
|
|
)
|
|
|
|
pi.DestroyQPainter()
|
|
|
|
|
|
|
|
editor.putLog("finished creating word cache:", text)
|
|
|
|
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
2021-10-01 20:47:08 +09:00
|
|
|
func (w *Window) drawTextInPos(p *gui.QPainter, x, y int, text string, highlight *Highlight, isNormalWidth bool) {
|
2021-05-02 18:08:37 +09:00
|
|
|
if text == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
font := p.Font()
|
|
|
|
fg := highlight.fg()
|
|
|
|
p.SetPen2(fg.QColor())
|
|
|
|
wsfont := w.getFont()
|
|
|
|
|
|
|
|
if highlight.bold {
|
|
|
|
font.SetWeight(wsfont.fontNew.Weight() + 25)
|
|
|
|
} else {
|
|
|
|
font.SetWeight(wsfont.fontNew.Weight())
|
|
|
|
}
|
|
|
|
if highlight.italic {
|
|
|
|
font.SetItalic(true)
|
|
|
|
} else {
|
|
|
|
font.SetItalic(false)
|
|
|
|
}
|
2021-10-01 20:47:08 +09:00
|
|
|
// p.DrawText(point, text)
|
|
|
|
p.DrawText3(x, y, text)
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawForeground(p *gui.QPainter, y int, col int, cols int) {
|
|
|
|
if w.s.name == "minimap" {
|
|
|
|
w.drawMinimap(p, y, col, cols)
|
|
|
|
} else {
|
2021-06-25 23:49:04 +09:00
|
|
|
// w.drawText(p, y, col, cols)
|
|
|
|
w.drawText(p, y, 0, w.cols)
|
2021-05-02 18:08:37 +09:00
|
|
|
w.drawTextDecoration(p, y, col, cols)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) drawTextDecoration(p *gui.QPainter, y int, col int, cols int) {
|
|
|
|
if y >= len(w.content) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
line := w.content[y]
|
|
|
|
font := w.getFont()
|
2021-05-22 16:00:02 +09:00
|
|
|
|
|
|
|
// Set smooth scroll offset
|
2021-05-23 01:19:05 +09:00
|
|
|
scrollPixels := 0
|
|
|
|
if w.lastScrollphase != core.Qt__NoScrollPhase {
|
|
|
|
scrollPixels = w.scrollPixels2
|
|
|
|
}
|
2021-05-22 16:00:02 +09:00
|
|
|
if editor.config.Editor.LineToScroll == 1 {
|
|
|
|
scrollPixels += w.scrollPixels[1]
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
for x := col; x <= col+cols; x++ {
|
|
|
|
if x >= len(line) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if line[x] == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !line[x].highlight.underline && !line[x].highlight.undercurl && !line[x].highlight.strikethrough {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// if CachedDrawing is disabled
|
|
|
|
if !editor.config.Editor.CachedDrawing {
|
|
|
|
pen := gui.NewQPen()
|
|
|
|
var color *gui.QColor
|
|
|
|
sp := line[x].highlight.special
|
|
|
|
if sp != nil {
|
|
|
|
color = sp.QColor()
|
|
|
|
pen.SetColor(color)
|
|
|
|
} else {
|
|
|
|
fg := line[x].highlight.foreground
|
|
|
|
color = fg.QColor()
|
|
|
|
pen.SetColor(color)
|
|
|
|
}
|
|
|
|
p.SetPen(pen)
|
|
|
|
start := float64(x) * font.truewidth
|
|
|
|
end := float64(x+1) * font.truewidth
|
|
|
|
|
|
|
|
space := float64(font.lineSpace) / 3.0
|
|
|
|
if space > font.ascent/3.0 {
|
|
|
|
space = font.ascent / 3.0
|
|
|
|
}
|
|
|
|
descent := float64(font.height) - font.ascent
|
|
|
|
weight := int(math.Ceil(float64(font.height) / 16.0))
|
|
|
|
if weight < 1 {
|
|
|
|
weight = 1
|
|
|
|
}
|
|
|
|
if line[x].highlight.strikethrough {
|
|
|
|
// strikeLinef := core.NewQLineF3(start, halfY, end, halfY)
|
|
|
|
// p.DrawLine(strikeLinef)
|
2021-05-22 16:00:02 +09:00
|
|
|
Y := float64(y*font.lineHeight+scrollPixels) + float64(font.ascent)*0.65 + float64(font.lineSpace/2)
|
2021-05-02 18:08:37 +09:00
|
|
|
p.FillRect5(
|
|
|
|
int(start),
|
|
|
|
int(Y),
|
|
|
|
int(math.Ceil(font.truewidth)),
|
|
|
|
weight,
|
|
|
|
color,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if line[x].highlight.underline {
|
|
|
|
// linef := core.NewQLineF3(start, Y, end, Y)
|
|
|
|
// p.DrawLine(linef)
|
|
|
|
p.FillRect5(
|
|
|
|
int(start),
|
2021-05-22 16:00:02 +09:00
|
|
|
int(float64((y+1)*font.lineHeight+scrollPixels))-weight,
|
2021-05-02 18:08:37 +09:00
|
|
|
int(math.Ceil(font.truewidth)),
|
|
|
|
weight,
|
|
|
|
color,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if line[x].highlight.undercurl {
|
|
|
|
amplitude := descent*0.65 + float64(font.lineSpace)
|
|
|
|
maxAmplitude := font.ascent / 8.0
|
|
|
|
if amplitude >= maxAmplitude {
|
|
|
|
amplitude = maxAmplitude
|
|
|
|
}
|
|
|
|
freq := 1.0
|
|
|
|
phase := 0.0
|
2021-05-22 16:00:02 +09:00
|
|
|
Y := float64(y*font.lineHeight+scrollPixels) + float64(font.ascent+descent*0.3) + float64(font.lineSpace/2) + space
|
2021-05-02 18:08:37 +09:00
|
|
|
Y2 := Y + amplitude*math.Sin(0)
|
|
|
|
point := core.NewQPointF3(start, Y2)
|
|
|
|
path := gui.NewQPainterPath2(point)
|
|
|
|
for i := int(point.X()); i <= int(end); i++ {
|
|
|
|
Y2 = Y + amplitude*math.Sin(2*math.Pi*freq*float64(i)/font.truewidth+phase)
|
|
|
|
path.LineTo(core.NewQPointF3(float64(i), Y2))
|
|
|
|
}
|
|
|
|
p.DrawPath(path)
|
|
|
|
}
|
2021-05-18 21:29:21 +09:00
|
|
|
} else { // if CachedDrawing is enabled
|
2021-05-02 18:08:37 +09:00
|
|
|
fgCache := w.getCache()
|
|
|
|
var image *gui.QImage
|
|
|
|
imagev, err := fgCache.get(HlDecoration{
|
|
|
|
fg: line[x].highlight.fg(),
|
|
|
|
underline: line[x].highlight.underline,
|
|
|
|
undercurl: line[x].highlight.undercurl,
|
|
|
|
strikethrough: line[x].highlight.strikethrough,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
image = w.newDecorationCache(line[x].char, line[x].highlight, line[x].normalWidth)
|
|
|
|
w.setDecorationCache(line[x].highlight, image)
|
|
|
|
} else {
|
|
|
|
image = imagev.(*gui.QImage)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.DrawImage7(
|
|
|
|
core.NewQPointF3(
|
|
|
|
float64(x)*font.truewidth,
|
2021-05-22 16:00:02 +09:00
|
|
|
float64(y*font.lineHeight)+float64(scrollPixels),
|
2021-05-02 18:08:37 +09:00
|
|
|
),
|
|
|
|
image,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) getFillpatternAndTransparent(hl *Highlight) (core.Qt__BrushStyle, *RGBA, int) {
|
|
|
|
color := hl.bg()
|
|
|
|
pattern := core.Qt__BrushStyle(1)
|
|
|
|
t := 255
|
2021-09-26 21:53:52 +09:00
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
// if pumblend > 0
|
|
|
|
if w.isPopupmenu {
|
2021-09-26 21:53:52 +09:00
|
|
|
// t = int((transparent() * 255.0) * ((100.0 - float64(w.s.ws.pb)) / 100.0))
|
|
|
|
// NOTE:
|
2021-10-08 23:49:45 +09:00
|
|
|
// We do not use the editor's transparency for completion menus or float windows.
|
2021-09-26 21:53:52 +09:00
|
|
|
// It is recommended to use pumblend or winblend to get those transparencies.
|
|
|
|
t = int(255 * ((100.0 - float64(w.s.ws.pb)) / 100.0))
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
// if winblend > 0
|
2021-09-26 21:53:52 +09:00
|
|
|
if !w.isPopupmenu && w.isFloatWin {
|
|
|
|
// t = int((transparent() * 255.0) * ((100.0 - float64(w.wb)) / 100.0))
|
|
|
|
// NOTE:
|
2021-10-08 23:49:45 +09:00
|
|
|
// We do not use the editor's transparency for completion menus or float windows.
|
2021-09-26 21:53:52 +09:00
|
|
|
// It is recommended to use pumblend or winblend to get those transparencies.
|
|
|
|
t = int(255 * ((100.0 - float64(w.wb)) / 100.0))
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
2021-09-25 15:23:31 +09:00
|
|
|
if w.isMsgGrid {
|
|
|
|
if editor.config.Message.Transparent < 1.0 {
|
|
|
|
t = int(editor.config.Message.Transparent * 255.0)
|
|
|
|
} else {
|
|
|
|
t = 255
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if editor.config.Editor.DiffChangePattern != 1 && hl.hlName == "DiffChange" {
|
|
|
|
pattern = core.Qt__BrushStyle(editor.config.Editor.DiffChangePattern)
|
|
|
|
if editor.config.Editor.DiffChangePattern >= 7 &&
|
|
|
|
editor.config.Editor.DiffChangePattern <= 14 {
|
|
|
|
t = int(editor.config.Editor.Transparent * 255)
|
|
|
|
}
|
|
|
|
color = color.HSV().Colorfulness().RGB()
|
|
|
|
} else if editor.config.Editor.DiffDeletePattern != 1 && hl.hlName == "DiffDelete" {
|
|
|
|
pattern = core.Qt__BrushStyle(editor.config.Editor.DiffDeletePattern)
|
|
|
|
if editor.config.Editor.DiffDeletePattern >= 7 &&
|
|
|
|
editor.config.Editor.DiffDeletePattern <= 14 {
|
|
|
|
t = int(editor.config.Editor.Transparent * 255)
|
|
|
|
}
|
|
|
|
color = color.HSV().Colorfulness().RGB()
|
|
|
|
} else if editor.config.Editor.DiffAddPattern != 1 && hl.hlName == "DiffAdd" {
|
|
|
|
pattern = core.Qt__BrushStyle(editor.config.Editor.DiffAddPattern)
|
|
|
|
if editor.config.Editor.DiffAddPattern >= 7 &&
|
|
|
|
editor.config.Editor.DiffAddPattern <= 14 {
|
|
|
|
t = int(editor.config.Editor.Transparent * 255)
|
|
|
|
}
|
|
|
|
color = color.HSV().Colorfulness().RGB()
|
|
|
|
}
|
|
|
|
|
|
|
|
return pattern, color, t
|
|
|
|
}
|
|
|
|
|
|
|
|
func isCJK(char rune) bool {
|
|
|
|
if unicode.Is(unicode.Han, char) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if unicode.Is(unicode.Hiragana, char) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if unicode.Is(unicode.Katakana, char) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if unicode.Is(unicode.Hangul, char) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// isNormalWidth is:
|
|
|
|
// On Windows, HorizontalAdvance() may take a long time to get the width of CJK characters.
|
|
|
|
// For this reason, for CJK characters, the character width should be the double width of ASCII characters.
|
|
|
|
// This issue may also be related to the following.
|
|
|
|
// https://github.com/equalsraf/neovim-qt/issues/614
|
|
|
|
func (w *Window) isNormalWidth(char string) bool {
|
|
|
|
if len(char) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// if ASCII
|
|
|
|
if char[0] <= 127 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// if CJK
|
|
|
|
if isCJK([]rune(char)[0]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return w.getFont().fontMetrics.HorizontalAdvance(char, -1) == w.getFont().truewidth
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) deleteExternalWin() {
|
|
|
|
if w.extwin != nil {
|
|
|
|
w.extwin.Hide()
|
|
|
|
w.extwin = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) setResizableForExtWin() {
|
|
|
|
if !w.extwinConnectResizable {
|
|
|
|
w.extwin.ConnectResizeEvent(func(event *gui.QResizeEvent) {
|
|
|
|
height := w.extwin.Height() - EXTWINBORDERSIZE*2
|
|
|
|
width := w.extwin.Width() - EXTWINBORDERSIZE*2
|
|
|
|
cols := int((float64(width) / w.getFont().truewidth))
|
|
|
|
rows := height / w.getFont().lineHeight
|
|
|
|
w.extwinResized = true
|
|
|
|
w.extwinManualResized = true
|
|
|
|
_ = w.s.ws.nvim.TryResizeUIGrid(w.grid, cols, rows)
|
|
|
|
})
|
|
|
|
w.extwinConnectResizable = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) getCache() Cache {
|
|
|
|
if w.font != nil {
|
|
|
|
return w.fgCache
|
|
|
|
}
|
|
|
|
|
|
|
|
return w.s.fgCache
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWindow() *Window {
|
|
|
|
// widget := widgets.NewQWidget(nil, 0)
|
|
|
|
win := NewWindow(nil, 0)
|
|
|
|
win.SetContentsMargins(0, 0, 0, 0)
|
|
|
|
win.SetAttribute(core.Qt__WA_OpaquePaintEvent, true)
|
|
|
|
win.SetStyleSheet(" * { background-color: rgba(0, 0, 0, 0);}")
|
|
|
|
win.scrollRegion = []int{0, 0, 0, 0}
|
|
|
|
win.background = editor.colors.bg
|
|
|
|
|
|
|
|
win.ConnectPaintEvent(win.paint)
|
2021-09-20 01:20:41 +09:00
|
|
|
win.SetAcceptDrops(true)
|
|
|
|
win.ConnectDragEnterEvent(win.dragEnterEvent)
|
|
|
|
win.ConnectDragMoveEvent(win.dragMoveEvent)
|
|
|
|
win.ConnectDropEvent(win.dropEvent)
|
2021-10-10 16:47:51 +09:00
|
|
|
win.ConnectMousePressEvent(win.mouseEvent)
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
return win
|
|
|
|
}
|
|
|
|
|
2021-10-10 16:47:51 +09:00
|
|
|
func (w *Window) mouseEvent(event *gui.QMouseEvent) {
|
|
|
|
bt := event.Button()
|
|
|
|
if event.Type() == core.QEvent__MouseMove {
|
|
|
|
if event.Buttons()&core.Qt__LeftButton > 0 {
|
|
|
|
bt = core.Qt__LeftButton
|
|
|
|
} else if event.Buttons()&core.Qt__RightButton > 0 {
|
|
|
|
bt = core.Qt__RightButton
|
|
|
|
} else if event.Buttons()&core.Qt__MidButton > 0 {
|
|
|
|
bt = core.Qt__MidButton
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
button := ""
|
|
|
|
switch bt {
|
|
|
|
case core.Qt__LeftButton:
|
|
|
|
button += "left"
|
|
|
|
case core.Qt__RightButton:
|
|
|
|
button += "right"
|
|
|
|
case core.Qt__MidButton:
|
|
|
|
button += "middle"
|
|
|
|
case core.Qt__NoButton:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
action := ""
|
|
|
|
switch event.Type() {
|
|
|
|
case core.QEvent__MouseButtonDblClick:
|
|
|
|
action = "press"
|
|
|
|
case core.QEvent__MouseButtonPress:
|
|
|
|
action = "press"
|
|
|
|
case core.QEvent__MouseButtonRelease:
|
|
|
|
action = "release"
|
|
|
|
case core.QEvent__MouseMove:
|
|
|
|
action = "drag"
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
mod := editor.modPrefix(event.Modifiers())
|
|
|
|
|
|
|
|
font := w.getFont()
|
|
|
|
col := int(float64(event.X()) / font.truewidth)
|
|
|
|
row := int(float64(event.Y()) / float64(font.lineHeight))
|
|
|
|
|
|
|
|
w.s.ws.nvim.InputMouse(button, action, mod, w.grid, row, col)
|
|
|
|
}
|
|
|
|
|
2021-09-20 01:20:41 +09:00
|
|
|
func (w *Window) dragEnterEvent(e *gui.QDragEnterEvent) {
|
|
|
|
e.AcceptProposedAction()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) dragMoveEvent(e *gui.QDragMoveEvent) {
|
|
|
|
e.AcceptProposedAction()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) dropEvent(e *gui.QDropEvent) {
|
|
|
|
e.SetDropAction(core.Qt__CopyAction)
|
|
|
|
e.AcceptProposedAction()
|
|
|
|
e.SetAccepted(true)
|
|
|
|
|
|
|
|
w.focusGrid()
|
|
|
|
|
|
|
|
for _, i := range strings.Split(e.MimeData().Text(), "\n") {
|
|
|
|
data := strings.Split(i, "://")
|
|
|
|
if i != "" {
|
|
|
|
switch data[0] {
|
|
|
|
case "file":
|
|
|
|
buf, _ := w.s.ws.nvim.CurrentBuffer()
|
|
|
|
bufName, _ := w.s.ws.nvim.BufferName(buf)
|
|
|
|
var filepath string
|
|
|
|
switch data[1][0] {
|
|
|
|
case '/':
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
filepath = strings.Trim(data[1], `/`)
|
|
|
|
} else {
|
|
|
|
filepath = data[1]
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
filepath = fmt.Sprintf(`//%s`, data[1])
|
|
|
|
} else {
|
|
|
|
filepath = data[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if bufName != "" {
|
|
|
|
w.s.howToOpen(filepath)
|
|
|
|
} else {
|
|
|
|
fileOpenInBuf(filepath)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:08:37 +09:00
|
|
|
func (w *Window) isShown() bool {
|
|
|
|
if w == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return w.IsVisible()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) raise() {
|
|
|
|
if w.grid == 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Raise()
|
|
|
|
|
|
|
|
font := w.getFont()
|
|
|
|
w.s.ws.cursor.updateFont(font)
|
|
|
|
w.s.ws.cursor.isInPalette = false
|
|
|
|
if !w.isExternal {
|
|
|
|
editor.window.Raise()
|
2021-06-10 23:24:19 +09:00
|
|
|
w.s.ws.cursor.SetParent(w.s.ws.widget)
|
2021-05-02 18:08:37 +09:00
|
|
|
} else if w.isExternal {
|
|
|
|
w.extwin.Raise()
|
2021-05-27 21:41:50 +09:00
|
|
|
w.s.ws.cursor.SetParent(w.extwin)
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
2021-05-27 21:41:50 +09:00
|
|
|
w.s.ws.cursor.Raise()
|
|
|
|
w.s.ws.cursor.Hide()
|
|
|
|
w.s.ws.cursor.Show()
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) show() {
|
|
|
|
w.fill()
|
|
|
|
w.Show()
|
2021-07-04 23:31:10 +09:00
|
|
|
|
|
|
|
// set buffer local ts value
|
|
|
|
if w.s.ws.ts != w.ts {
|
2021-07-22 13:21:40 +09:00
|
|
|
w.s.ws.optionsetMutex.Lock()
|
|
|
|
w.s.ws.ts = w.ts
|
|
|
|
w.s.ws.optionsetMutex.Unlock()
|
2021-07-04 23:31:10 +09:00
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) hide() {
|
|
|
|
w.Hide()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) setGridGeometry(width, height int) {
|
|
|
|
if w.isExternal && !w.extwinResized {
|
|
|
|
w.extwin.Resize2(width+EXTWINBORDERSIZE*2, height+EXTWINBORDERSIZE*2)
|
|
|
|
}
|
|
|
|
w.extwinResized = false
|
|
|
|
|
|
|
|
rect := core.NewQRect4(0, 0, width, height)
|
|
|
|
w.SetGeometry(rect)
|
|
|
|
w.fill()
|
|
|
|
}
|
|
|
|
|
2021-10-08 23:49:45 +09:00
|
|
|
// refreshUpdateArea:: arg:0 => full, arg:1 => full only text
|
2021-05-27 17:38:37 +09:00
|
|
|
func (w *Window) refreshUpdateArea(fullmode int) {
|
|
|
|
var boundary int
|
|
|
|
if fullmode == 0 {
|
|
|
|
boundary = w.cols
|
|
|
|
} else {
|
|
|
|
boundary = w.maxLenContent
|
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
for i := 0; i < len(w.lenContent); i++ {
|
2021-05-27 17:38:37 +09:00
|
|
|
w.lenContent[i] = boundary
|
2021-05-02 18:08:37 +09:00
|
|
|
for j, _ := range w.contentMask[i] {
|
|
|
|
w.contentMask[i][j] = true
|
|
|
|
}
|
|
|
|
}
|
2021-05-27 17:38:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) fill() {
|
|
|
|
w.refreshUpdateArea(0)
|
2021-05-02 18:08:37 +09:00
|
|
|
if editor.config.Editor.Transparent < 1.0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if w.isMsgGrid && editor.config.Message.Transparent < 1.0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// If popupmenu pumblend is set
|
|
|
|
if w.isPopupmenu && w.s.ws.pb > 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// If window winblend > 0 is set
|
|
|
|
if !w.isPopupmenu && w.isFloatWin && w.wb > 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if w.background != nil {
|
|
|
|
w.SetAutoFillBackground(true)
|
|
|
|
p := gui.NewQPalette()
|
|
|
|
p.SetColor2(gui.QPalette__Background, w.background.QColor())
|
|
|
|
w.SetPalette(p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) setShadow() {
|
|
|
|
if !editor.config.Editor.DrawShadowForFloatWindow {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.SetGraphicsEffect(util.DropShadow(0, 25, 125, 110))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) move(col int, row int) {
|
|
|
|
font := w.s.font
|
2021-05-08 16:04:01 +09:00
|
|
|
res := 0
|
|
|
|
if w.isMsgGrid {
|
2021-05-18 21:29:21 +09:00
|
|
|
res = w.s.widget.Height() - w.rows*font.lineHeight
|
2021-05-08 16:04:01 +09:00
|
|
|
}
|
|
|
|
if res < 0 {
|
2021-05-18 21:29:21 +09:00
|
|
|
res = 0
|
2021-05-08 16:04:01 +09:00
|
|
|
}
|
2021-05-02 18:08:37 +09:00
|
|
|
x := int(float64(col) * font.truewidth)
|
2021-05-08 16:04:01 +09:00
|
|
|
y := (row * font.lineHeight) + res
|
2021-05-02 18:08:37 +09:00
|
|
|
|
|
|
|
if w.isFloatWin {
|
|
|
|
if w.s.ws.drawTabline {
|
|
|
|
y += w.s.ws.tabline.widget.Height()
|
|
|
|
}
|
|
|
|
// A workarround for ext_popupmenu and displaying a LSP tooltip
|
|
|
|
if editor.config.Editor.ExtPopupmenu {
|
|
|
|
if w.s.ws.mode == "insert" && w.s.ws.popup.widget.IsVisible() {
|
|
|
|
if w.s.ws.popup.widget.IsVisible() {
|
|
|
|
w.SetGraphicsEffect(util.DropShadow(0, 25, 125, 110))
|
|
|
|
w.Move2(
|
|
|
|
w.s.ws.popup.widget.X()+w.s.ws.popup.widget.Width()+5,
|
|
|
|
w.s.ws.popup.widget.Y(),
|
|
|
|
)
|
2021-10-10 16:47:51 +09:00
|
|
|
w.raise()
|
2021-05-02 18:08:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if w.isExternal {
|
|
|
|
w.Move2(EXTWINBORDERSIZE, EXTWINBORDERSIZE)
|
|
|
|
w.layoutExternalWindow(x, y)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Move2(x, y)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Window) layoutExternalWindow(x, y int) {
|
|
|
|
font := w.s.font
|
|
|
|
|
|
|
|
// float windows width, height
|
|
|
|
width := int(float64(w.cols) * font.truewidth)
|
|
|
|
height := w.rows * font.lineHeight
|
|
|
|
dx := []int{}
|
|
|
|
dy := []int{}
|
|
|
|
|
|
|
|
// layout external windows
|
|
|
|
// Adjacent to each other through edges of the same length as much as possible.
|
|
|
|
if w.pos[0] == 0 && w.pos[1] == 0 && !w.extwinManualResized {
|
|
|
|
w.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 == w.grid {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if win.isExternal {
|
|
|
|
|
|
|
|
dc := 0
|
|
|
|
dr := 0
|
|
|
|
for _, e := range dx {
|
|
|
|
dc += e
|
|
|
|
}
|
|
|
|
for _, e := range dy {
|
|
|
|
dr += e
|
|
|
|
}
|
|
|
|
|
|
|
|
winx := 0
|
|
|
|
winy := 0
|
|
|
|
for _, e := range win.extwinAutoLayoutPosX {
|
|
|
|
winx += e
|
|
|
|
}
|
|
|
|
for _, e := range win.extwinAutoLayoutPosY {
|
|
|
|
winy += e
|
|
|
|
}
|
|
|
|
|
|
|
|
if winx <= w.pos[0]+dc && winx+win.cols > w.pos[0]+dc &&
|
|
|
|
winy <= w.pos[1]+dr && winy+win.rows > w.pos[1]+dr {
|
|
|
|
|
|
|
|
widthRatio := float64(w.cols+win.cols) * font.truewidth / float64(editor.window.Width())
|
|
|
|
heightRatio := float64((w.rows+win.rows)*font.lineHeight) / float64(editor.window.Height())
|
|
|
|
if w.cols == win.cols {
|
|
|
|
dy = append(dy, win.rows)
|
|
|
|
height += win.rows*font.lineHeight + EXTWINBORDERSIZE*2 + EXTWINMARGINSIZE
|
|
|
|
} else if w.rows == win.rows {
|
|
|
|
dx = append(dx, win.cols)
|
|
|
|
width += int(float64(win.cols)*font.truewidth) + EXTWINBORDERSIZE*2 + EXTWINMARGINSIZE
|
|
|
|
} else {
|
|
|
|
if widthRatio > heightRatio {
|
|
|
|
dy = append(dy, win.rows)
|
|
|
|
height += win.rows*font.lineHeight + EXTWINBORDERSIZE*2 + EXTWINMARGINSIZE
|
|
|
|
} else {
|
|
|
|
dx = append(dx, win.cols)
|
|
|
|
width += int(float64(win.cols)*font.truewidth) + EXTWINBORDERSIZE*2 + EXTWINMARGINSIZE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
x := 0
|
|
|
|
y := 0
|
|
|
|
for _, e := range dx {
|
|
|
|
x += int(float64(e)*font.truewidth) + EXTWINBORDERSIZE*2 + EXTWINMARGINSIZE
|
|
|
|
}
|
|
|
|
for _, e := range dy {
|
|
|
|
y += e*font.lineHeight + EXTWINBORDERSIZE*2 + EXTWINMARGINSIZE
|
|
|
|
}
|
|
|
|
w.extwinAutoLayoutPosX = dx
|
|
|
|
w.extwinAutoLayoutPosY = dy
|
|
|
|
w.extwinRelativePos = [2]int{editor.window.Pos().X() + x, editor.window.Pos().Y() + y}
|
|
|
|
}
|
|
|
|
|
|
|
|
w.extwin.Move2(editor.window.Pos().X()+x, editor.window.Pos().Y()+y)
|
|
|
|
|
|
|
|
// centering
|
|
|
|
if w.pos[0] == 0 && w.pos[1] == 0 && !w.extwinManualResized {
|
|
|
|
var newx, newy int
|
|
|
|
if editor.window.Width()-width > 0 {
|
|
|
|
newx = int(float64(editor.window.Width()-width)/2.0) - (EXTWINMARGINSIZE / 2)
|
|
|
|
}
|
|
|
|
if editor.window.Height()-height > 0 {
|
|
|
|
newy = int(float64(editor.window.Height()-height)/2.0) - (EXTWINMARGINSIZE / 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.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.isExternal && !win.extwinManualResized {
|
|
|
|
win.extwin.Move2(win.extwinRelativePos[0]+newx, win.extwinRelativePos[1]+newy)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|