diff --git a/editor/cmdline.go b/editor/cmdline.go index b5331021..49cbfe6f 100644 --- a/editor/cmdline.go +++ b/editor/cmdline.go @@ -15,6 +15,7 @@ type CmdContent struct { // Cmdline is the cmdline type Cmdline struct { + ws *Workspace pos int content *CmdContent preContent *CmdContent @@ -64,7 +65,7 @@ func (c *Cmdline) show(args []interface{}) { c.content.indent = indent c.content.prompt = prompt text := c.getText("") - palette := editor.palette + palette := c.ws.palette palette.setPattern(text) c.cursorMove() if !c.wildmenuShown { @@ -76,7 +77,7 @@ func (c *Cmdline) show(args []interface{}) { func (c *Cmdline) showAddition() { lines := append(c.getPromptLines(), c.getFunctionLines()...) - palette := editor.palette + palette := c.ws.palette for i, resultItem := range palette.resultItems { if i >= len(lines) || i >= palette.showTotal { resultItem.hide() @@ -117,11 +118,11 @@ func (c *Cmdline) getFunctionLines() []string { } func (c *Cmdline) cursorMove() { - editor.palette.cursorMove(c.pos + len(c.content.firstc) + c.content.indent) + c.ws.palette.cursorMove(c.pos + len(c.content.firstc) + c.content.indent) } func (c *Cmdline) hide(args []interface{}) { - palette := editor.palette + palette := c.ws.palette palette.hide() if c.inFunction { c.function = append(c.function, c.content) @@ -155,7 +156,7 @@ func (c *Cmdline) putChar(args []interface{}) { // level := reflectToInt(args[2]) // fmt.Println("putChar", ch, shift, level) text := c.getText(ch) - palette := editor.palette + palette := c.ws.palette palette.setPattern(text) } @@ -163,7 +164,7 @@ func (c *Cmdline) wildmenuShow(args []interface{}) { c.wildmenuShown = true args = args[0].([]interface{}) c.rawItems = args[0].([]interface{}) - palette := editor.palette + palette := c.ws.palette c.top = 0 for i := 0; i < palette.showTotal; i++ { resultItem := palette.resultItems[i] @@ -195,7 +196,7 @@ func (c *Cmdline) wildmenuShow(args []interface{}) { func (c *Cmdline) wildmenuSelect(args []interface{}) { selected := reflectToInt(args[0].([]interface{})[0]) // fmt.Println("selected is", selected) - showTotal := editor.palette.showTotal + showTotal := c.ws.palette.showTotal if selected == -1 && c.top > 0 { c.wildmenuScroll(-c.top) } @@ -205,7 +206,7 @@ func (c *Cmdline) wildmenuSelect(args []interface{}) { if selected >= 0 && selected-c.top < 0 { c.wildmenuScroll(-1) } - palette := editor.palette + palette := c.ws.palette for i := 0; i < palette.showTotal; i++ { item := palette.resultItems[i] item.setSelected(selected == i+c.top) @@ -214,7 +215,7 @@ func (c *Cmdline) wildmenuSelect(args []interface{}) { func (c *Cmdline) wildmenuScroll(n int) { c.top += n - palette := editor.palette + palette := c.ws.palette for i := 0; i < palette.showTotal; i++ { resultItem := palette.resultItems[i] if i >= len(c.rawItems) { diff --git a/editor/cursor.go b/editor/cursor.go index f47b46f7..295b3310 100644 --- a/editor/cursor.go +++ b/editor/cursor.go @@ -7,6 +7,7 @@ import ( // Cursor is type Cursor struct { + ws *Workspace widget *widgets.QWidget mode string x int @@ -25,31 +26,31 @@ func initCursorNew() *Cursor { func (c *Cursor) move() { c.widget.Move2(c.x, c.y) - editor.loc.widget.Move2(c.x, c.y+editor.font.lineHeight) + c.ws.loc.widget.Move2(c.x, c.y+c.ws.font.lineHeight) } func (c *Cursor) updateShape() { - mode := editor.mode + mode := c.ws.mode if mode == "normal" { - c.widget.Resize2(editor.font.width, editor.font.lineHeight) + c.widget.Resize2(c.ws.font.width, c.ws.font.lineHeight) c.widget.SetStyleSheet("background-color: rgba(255, 255, 255, 0.5)") } else if mode == "insert" { - c.widget.Resize2(1, editor.font.lineHeight) + c.widget.Resize2(1, c.ws.font.lineHeight) c.widget.SetStyleSheet("background-color: rgba(255, 255, 255, 0.9)") } } func (c *Cursor) update() { - if c.mode != editor.mode { - c.mode = editor.mode + if c.mode != c.ws.mode { + c.mode = c.ws.mode c.updateShape() } - row := editor.screen.cursor[0] - col := editor.screen.cursor[1] + row := c.ws.screen.cursor[0] + col := c.ws.screen.cursor[1] if c.row != row || c.col != col { - c.x = int(float64(col) * editor.font.truewidth) - c.y = row * editor.font.lineHeight + c.x = int(float64(col) * c.ws.font.truewidth) + c.y = row * c.ws.font.lineHeight c.move() } - editor.screen.tooltip.Move(core.NewQPoint2(c.x, c.y)) + c.ws.screen.tooltip.Move(core.NewQPoint2(c.x, c.y)) } diff --git a/editor/editor.go b/editor/editor.go index 719a85f3..b3195cde 100644 --- a/editor/editor.go +++ b/editor/editor.go @@ -3,14 +3,16 @@ package editor import ( "fmt" "os" + "path/filepath" "runtime" "strconv" "strings" "sync" - "github.com/dzhou121/gonvim/fuzzy" + homedir "github.com/mitchellh/go-homedir" "github.com/neovim/go-client/nvim" "github.com/therecipe/qt/core" + "github.com/therecipe/qt/gui" "github.com/therecipe/qt/widgets" ) @@ -31,43 +33,34 @@ type Char struct { // Editor is the editor type Editor struct { - app *widgets.QApplication - nvim *nvim.Nvim - window *widgets.QMainWindow - nvimAttached bool - mode string - font *Font - smallerFont *Font - rows int - cols int - cursorNew *Cursor - Foreground *RGBA - Background *RGBA - special *RGBA - screen *Screen - close chan bool - loc *Locpopup - signature *Signature - popup *PopupMenu - finder *Finder - cmdline *Cmdline - palette *Palette - tabline *Tabline - statusline *Statusline - message *Message - drawStatusline bool - drawTabline bool - drawLint bool + app *widgets.QApplication + workspaces []*Workspace + active int + nvim *nvim.Nvim + window *widgets.QMainWindow + wsWidget *widgets.QWidget + wsSide *WorkspaceSide + statuslineHeight int width int height int tablineHeight int selectedBg *RGBA matchFg *RGBA - resizeMutex sync.Mutex - signal *editorSignal - redrawUpdates chan [][]interface{} - guiUpdates chan []interface{} + + stop chan struct{} + stopOnce sync.Once + + specialKeys map[core.Qt__Key]string + controlModifier core.Qt__KeyboardModifier + cmdModifier core.Qt__KeyboardModifier + shiftModifier core.Qt__KeyboardModifier + altModifier core.Qt__KeyboardModifier + metaModifier core.Qt__KeyboardModifier + keyControl core.Qt__Key + keyCmd core.Qt__Key + keyAlt core.Qt__Key + keyShift core.Qt__Key } type editorSignal struct { @@ -81,452 +74,383 @@ type editorSignal struct { _ func() `signal:"messageSignal"` } -func (e *Editor) handleNotification() { - e.nvim.RegisterHandler("Gui", func(updates ...interface{}) { - e.guiUpdates <- updates - e.signal.GuiSignal() - }) - e.nvim.RegisterHandler("redraw", func(updates ...[]interface{}) { - e.redrawUpdates <- updates - e.signal.RedrawSignal() - }) +func (hl *Highlight) copy() Highlight { + highlight := Highlight{} + if hl.foreground != nil { + highlight.foreground = hl.foreground.copy() + } + if hl.background != nil { + highlight.background = hl.background.copy() + } + return highlight } -func (e *Editor) handleRPCGui(updates []interface{}) { - event := updates[0].(string) - switch event { - case "Font": - e.guiFont(updates[1:]) - case "Linespace": - e.guiLinespace(updates[1:]) - case "finder_pattern": - e.finder.showPattern(updates[1:]) - case "finder_pattern_pos": - e.finder.cursorPos(updates[1:]) - case "finder_show_result": - e.finder.showResult(updates[1:]) - case "finder_hide": - e.finder.hide() - case "finder_select": - e.finder.selectResult(updates[1:]) - case "signature_show": - e.signature.showItem(updates[1:]) - case "signature_pos": - e.signature.pos(updates[1:]) - case "signature_hide": - e.signature.hide() - default: - fmt.Println("unhandled Gui event", event) +// InitEditor is +func InitEditor() { + editor = &Editor{ + selectedBg: newRGBA(81, 154, 186, 0.5), + matchFg: newRGBA(81, 154, 186, 1), + stop: make(chan struct{}), } -} + e := editor + e.app = widgets.NewQApplication(0, nil) + e.app.ConnectAboutToQuit(func() { + editor.cleanup() + }) + e.width = 800 + e.height = 600 -func (e *Editor) handleRedraw(updates [][]interface{}) { - s := e.screen - for _, update := range updates { - event := update[0].(string) - args := update[1:] - switch event { - case "update_fg": - args := update[1].([]interface{}) - color := reflectToInt(args[0]) - if color == -1 { - editor.Foreground = newRGBA(255, 255, 255, 1) - } else { - editor.Foreground = calcColor(reflectToInt(args[0])) - } - case "update_bg": - args := update[1].([]interface{}) - s.updateBg(args) - case "update_sp": - args := update[1].([]interface{}) - color := reflectToInt(args[0]) - if color == -1 { - editor.special = newRGBA(255, 255, 255, 1) - } else { - editor.special = calcColor(reflectToInt(args[0])) + //create a window + e.window = widgets.NewQMainWindow(nil, 0) + e.window.SetWindowTitle("Gonvim") + e.window.SetContentsMargins(0, 0, 0, 0) + e.window.SetMinimumSize2(e.width, e.height) + + e.initSpecialKeys() + e.window.ConnectKeyPressEvent(e.keyPress) + + // e.window.SetAttribute(core.Qt__WA_InputMethodEnabled, true) + // e.window.ConnectInputMethodEvent(screen.InputMethodEvent) + // e.window.ConnectInputMethodQuery(screen.InputMethodQuery) + e.window.SetAcceptDrops(true) + + layout := widgets.NewQHBoxLayout() + widget := widgets.NewQWidget(nil, 0) + widget.SetContentsMargins(0, 0, 0, 0) + widget.SetLayout(layout) + e.wsWidget = widgets.NewQWidget(nil, 0) + e.wsSide = newWorkspaceSide() + layout.AddWidget(e.wsSide.widget, 0, 0) + layout.AddWidget(e.wsWidget, 1, 0) + layout.SetContentsMargins(0, 0, 0, 0) + layout.SetSpacing(0) + + e.workspaces = []*Workspace{} + sessionExists := false + home, err := homedir.Dir() + if err == nil { + for i := 0; i < 20; i++ { + path := filepath.Join(home, ".gonvim", "sessions", strconv.Itoa(i)+".vim") + _, err := os.Stat(path) + if err != nil { + break } - case "cursor_goto": - s.cursorGoto(args) - case "put": - s.put(args) - case "eol_clear": - s.eolClear(args) - case "clear": - s.clear(args) - case "resize": - s.resize(args) - case "highlight_set": - s.highlightSet(args) - case "set_scroll_region": - s.setScrollRegion(args) - case "scroll": - s.scroll(args) - case "mode_change": - arg := update[len(update)-1].([]interface{}) - editor.mode = arg[0].(string) - case "popupmenu_show": - editor.popup.showItems(args) - case "popupmenu_hide": - editor.popup.hide() - case "popupmenu_select": - editor.popup.selectItem(args) - case "tabline_update": - editor.tabline.update(args) - case "cmdline_show": - editor.cmdline.show(args) - case "cmdline_pos": - editor.cmdline.changePos(args) - case "cmdline_char": - editor.cmdline.putChar(args) - case "cmdline_hide": - editor.cmdline.hide(args) - case "cmdline_function_show": - editor.cmdline.functionShow() - case "cmdline_function_hide": - editor.cmdline.functionHide() - case "wildmenu_show": - editor.cmdline.wildmenuShow(args) - case "wildmenu_select": - editor.cmdline.wildmenuSelect(args) - case "wildmenu_hide": - editor.cmdline.wildmenuHide() - case "msg_start_kind": - if len(args) > 0 { - kinds, ok := args[len(args)-1].([]interface{}) - if ok { - if len(kinds) > 0 { - kind, ok := kinds[len(kinds)-1].(string) - if ok { - editor.message.kind = kind - } - } - } + sessionExists = true + ws, err := newWorkspace(path) + if err != nil { + break } - case "msg_chunk": - editor.message.chunk(args) - case "msg_end": - case "msg_showcmd": - case "messages": - case "busy_start": - case "busy_stop": - default: - fmt.Println("Unhandle event", event) + e.workspaces = append(e.workspaces, ws) + e.workspaceUpdate() + } + } + if !sessionExists { + ws, err := newWorkspace("") + if err != nil { + return } + e.workspaces = append(e.workspaces, ws) + e.workspaceUpdate() } - s.update() - editor.cursorNew.update() - editor.statusline.mode.redraw() + + e.wsWidget.ConnectResizeEvent(func(event *gui.QResizeEvent) { + for _, ws := range e.workspaces { + ws.updateSize() + } + }) + + e.window.SetCentralWidget(widget) + + go func() { + <-editor.stop + e.app.Quit() + }() + + e.window.Show() + // for i := len(e.workspaces) - 1; i >= 0; i-- { + // e.active = i + // } + widgets.QApplication_Exec() } -func (e *Editor) guiFont(args ...interface{}) { - fontArg := args[0].([]interface{}) - parts := strings.Split(fontArg[0].(string), ":") - if len(parts) < 1 { +func (e *Editor) workspaceNew() { + ws, err := newWorkspace("") + if err != nil { return } + e.active++ + e.workspaces = append(e.workspaces, nil) + copy(e.workspaces[e.active+1:], e.workspaces[e.active:]) + e.workspaces[e.active] = ws + e.workspaceUpdate() +} - 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 - } - } +func (e *Editor) workspaceSwitch(index int) { + index-- + if index < 0 || index >= len(e.workspaces) { + return } + e.active = index + e.workspaceUpdate() +} - e.font.change(parts[0], height) - e.nvimResize() - e.popup.updateFont(e.font) +func (e *Editor) workspaceNext() { + e.active++ + if e.active >= len(e.workspaces) { + e.active = 0 + } + e.workspaceUpdate() } -func (e *Editor) guiLinespace(args ...interface{}) { - fontArg := args[0].([]interface{}) - var lineSpace int - var err error - switch arg := fontArg[0].(type) { - case string: - lineSpace, err = strconv.Atoi(arg) - if err != nil { - return - } - case int32: // can't combine these in a type switch without compile error - lineSpace = int(arg) - case int64: - lineSpace = int(arg) - default: - return +func (e *Editor) workspacePrevious() { + e.active-- + if e.active < 0 { + e.active = len(e.workspaces) - 1 } - e.font.changeLineSpace(lineSpace) - e.nvimResize() + e.workspaceUpdate() } -func (e *Editor) nvimResize() { - // e.screen.paintMutex.Lock() - // defer e.screen.paintMutex.Unlock() - width, height := e.screen.width, e.screen.height - height += e.tabline.marginTop - e.tabline.marginDefault + e.tabline.marginBottom - e.tabline.marginDefault - cols := int(float64(width) / editor.font.truewidth) - rows := height / editor.font.lineHeight - oldCols := editor.cols - oldRows := editor.rows - editor.cols = cols - editor.rows = rows - remainingHeight := height - rows*editor.font.lineHeight - remainingHeightBottom := remainingHeight / 2 - remainingHeightTop := remainingHeight - remainingHeightBottom - e.tabline.marginTop = e.tabline.marginDefault + remainingHeightTop - e.tabline.marginBottom = e.tabline.marginDefault + remainingHeightBottom - e.tabline.updateMargin() - if oldCols > 0 && oldRows > 0 { - if cols != oldCols || rows != oldRows { - editor.nvim.TryResizeUI(cols, rows) +func (e *Editor) workspaceUpdate() { + for i, ws := range e.workspaces { + if i == e.active { + ws.hide() + ws.show() + } else { + ws.hide() } } -} -func (hl *Highlight) copy() Highlight { - highlight := Highlight{} - if hl.foreground != nil { - highlight.foreground = hl.foreground.copy() + for i := 0; i < len(e.wsSide.items) && i < len(e.workspaces); i++ { + if i == e.active { + e.wsSide.items[i].setActive() + } else { + e.wsSide.items[i].setInactive() + } + e.wsSide.items[i].setText(e.workspaces[i].cwdBase) + e.wsSide.items[i].show() } - if hl.background != nil { - highlight.background = hl.background.copy() + for i := len(e.workspaces); i < len(e.wsSide.items); i++ { + e.wsSide.items[i].hide() + } + if len(e.workspaces) == 1 { + e.wsSide.items[0].hide() } - return highlight } -func (e *Editor) configure() { - var drawSplit interface{} - e.nvim.Var("gonvim_draw_split", &drawSplit) - if isZero(drawSplit) { - e.screen.drawSplit = false - } else { - e.screen.drawSplit = true +func (e *Editor) keyPress(event *gui.QKeyEvent) { + input := e.convertKey(event.Text(), event.Key(), event.Modifiers()) + if input != "" { + e.workspaces[e.active].nvim.Input(input) } +} - var drawStatusline interface{} - e.nvim.Var("gonvim_draw_statusline", &drawStatusline) - if isZero(drawStatusline) { - e.drawStatusline = false - } else { - e.drawStatusline = true +func (e *Editor) convertKey(text string, key int, mod core.Qt__KeyboardModifier) string { + if mod&core.Qt__KeypadModifier > 0 { + switch core.Qt__Key(key) { + case core.Qt__Key_Home: + return fmt.Sprintf("<%sHome>", e.modPrefix(mod)) + case core.Qt__Key_End: + return fmt.Sprintf("<%sEnd>", e.modPrefix(mod)) + case core.Qt__Key_PageUp: + return fmt.Sprintf("<%sPageUp>", e.modPrefix(mod)) + case core.Qt__Key_PageDown: + return fmt.Sprintf("<%sPageDown>", e.modPrefix(mod)) + case core.Qt__Key_Plus: + return fmt.Sprintf("<%sPlus>", e.modPrefix(mod)) + case core.Qt__Key_Minus: + return fmt.Sprintf("<%sMinus>", e.modPrefix(mod)) + case core.Qt__Key_multiply: + return fmt.Sprintf("<%sMultiply>", e.modPrefix(mod)) + case core.Qt__Key_division: + return fmt.Sprintf("<%sDivide>", e.modPrefix(mod)) + case core.Qt__Key_Enter: + return fmt.Sprintf("<%sEnter>", e.modPrefix(mod)) + case core.Qt__Key_Period: + return fmt.Sprintf("<%sPoint>", e.modPrefix(mod)) + case core.Qt__Key_0: + return fmt.Sprintf("<%s0>", e.modPrefix(mod)) + case core.Qt__Key_1: + return fmt.Sprintf("<%s1>", e.modPrefix(mod)) + case core.Qt__Key_2: + return fmt.Sprintf("<%s2>", e.modPrefix(mod)) + case core.Qt__Key_3: + return fmt.Sprintf("<%s3>", e.modPrefix(mod)) + case core.Qt__Key_4: + return fmt.Sprintf("<%s4>", e.modPrefix(mod)) + case core.Qt__Key_5: + return fmt.Sprintf("<%s5>", e.modPrefix(mod)) + case core.Qt__Key_6: + return fmt.Sprintf("<%s6>", e.modPrefix(mod)) + case core.Qt__Key_7: + return fmt.Sprintf("<%s7>", e.modPrefix(mod)) + case core.Qt__Key_8: + return fmt.Sprintf("<%s8>", e.modPrefix(mod)) + case core.Qt__Key_9: + return fmt.Sprintf("<%s9>", e.modPrefix(mod)) + } } - var drawTabline interface{} - e.nvim.Var("gonvim_draw_tabline", &drawTabline) - if isZero(drawTabline) { - e.drawTabline = false - } else { - e.drawTabline = true + if text == "<" { + return "" } - var drawLint interface{} - e.nvim.Var("gonvim_draw_lint", &drawLint) - if isZero(drawLint) { - e.drawLint = false - } else { - e.drawLint = true + specialKey, ok := e.specialKeys[core.Qt__Key(key)] + if ok { + return fmt.Sprintf("<%s%s>", e.modPrefix(mod), specialKey) } - var startFullscreen interface{} - e.nvim.Var("gonvim_start_fullscreen", &startFullscreen) - if isTrue(startFullscreen) { - e.window.ShowFullScreen() + if text == "\\" { + return fmt.Sprintf("<%s%s>", e.modPrefix(mod), "Bslash") } -} -// InitEditor is -func InitEditor() { - app := widgets.NewQApplication(0, nil) - devicePixelRatio := app.DevicePixelRatio() - fontFamily := "" - switch runtime.GOOS { - case "windows": - fontFamily = "Consolas" - case "darwin": - fontFamily = "Courier New" - default: - fontFamily = "Monospace" + c := "" + if mod&e.controlModifier > 0 || mod&e.cmdModifier > 0 { + if int(e.keyControl) == key || int(e.keyCmd) == key || int(e.keyAlt) == key || int(e.keyShift) == key { + return "" + } + c = string(key) + if !(mod&e.shiftModifier > 0) { + c = strings.ToLower(c) + } + } else { + c = text } - font := initFontNew(fontFamily, 14, 6) - width := 800 - height := 600 + if c == "" { + return "" + } - //create a window - window := widgets.NewQMainWindow(nil, 0) - window.SetWindowTitle("Gonvim") - window.SetContentsMargins(0, 0, 0, 0) - window.SetMinimumSize2(width, height) - - tabline := initTablineNew() - statusline := initStatuslineNew() - screen := initScreenNew(devicePixelRatio) - screen.toolTipFont(font) - cursor := initCursorNew() - cursor.widget.SetParent(screen.widget) - popup := initPopupmenuNew(font) - popup.widget.SetParent(screen.widget) - finder := initFinder() - palette := initPalette() - palette.widget.SetParent(screen.widget) - loc := initLocpopup() - loc.widget.SetParent(screen.widget) - signature := initSignature() - signature.widget.SetParent(screen.widget) - message := initMessage() - message.widget.SetParent(screen.widget) - window.ConnectKeyPressEvent(screen.keyPress) - - window.SetAttribute(core.Qt__WA_InputMethodEnabled, true) - window.ConnectInputMethodEvent(screen.InputMethodEvent) - window.ConnectInputMethodQuery(screen.InputMethodQuery) - window.SetAcceptDrops(true) - - layout := widgets.NewQVBoxLayout() - widget := widgets.NewQWidget(nil, 0) - widget.SetContentsMargins(0, 0, 0, 0) - widget.SetLayout(layout) - layout.AddWidget(tabline.widget, 0, 0) - layout.AddWidget(screen.widget, 1, 0) - layout.AddWidget(statusline.widget, 0, 0) - layout.SetContentsMargins(0, 0, 0, 0) - layout.SetSpacing(0) + char := core.NewQChar10(c) + if char.Unicode() < 0x100 && !char.IsNumber() && char.IsPrint() { + mod &= ^e.shiftModifier + } - window.SetCentralWidget(widget) + prefix := e.modPrefix(mod) + if prefix != "" { + return fmt.Sprintf("<%s%s>", prefix, c) + } - neovim, err := nvim.NewEmbedded(&nvim.EmbedOptions{ - Args: os.Args[1:], - }) - if err != nil { - fmt.Println("nvim start error", err) - app.Quit() - return + return c +} + +func (e *Editor) modPrefix(mod core.Qt__KeyboardModifier) string { + prefix := "" + if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { + if mod&e.cmdModifier > 0 { + prefix += "D-" + } } - signal := NewEditorSignal(nil) + if mod&e.controlModifier > 0 { + prefix += "C-" + } - editor = &Editor{ - app: app, - nvim: neovim, - window: window, - nvimAttached: false, - screen: screen, - cursorNew: cursor, - mode: "normal", - close: make(chan bool), - popup: popup, - finder: finder, - cmdline: initCmdline(), - palette: palette, - loc: loc, - signature: signature, - tabline: tabline, - message: message, - width: width, - height: height, - statusline: statusline, - font: font, - selectedBg: newRGBA(81, 154, 186, 0.5), - matchFg: newRGBA(81, 154, 186, 1), - signal: signal, - redrawUpdates: make(chan [][]interface{}, 1000), - guiUpdates: make(chan []interface{}, 1000), + if mod&e.shiftModifier > 0 { + prefix += "S-" } - signal.ConnectRedrawSignal(func() { - updates := <-editor.redrawUpdates - editor.handleRedraw(updates) - }) + if mod&e.altModifier > 0 { + prefix += "A-" + } - signal.ConnectGuiSignal(func() { - updates := <-editor.guiUpdates - editor.handleRPCGui(updates) - }) + return prefix +} - editor.handleNotification() - // editor.finder.rePosition() - go func() { - err := neovim.Serve() - if err != nil { - fmt.Println(err) +func (e *Editor) initSpecialKeys() { + e.specialKeys = map[core.Qt__Key]string{} + e.specialKeys[core.Qt__Key_Up] = "Up" + e.specialKeys[core.Qt__Key_Down] = "Down" + e.specialKeys[core.Qt__Key_Left] = "Left" + e.specialKeys[core.Qt__Key_Right] = "Right" + + e.specialKeys[core.Qt__Key_F1] = "F1" + e.specialKeys[core.Qt__Key_F2] = "F2" + e.specialKeys[core.Qt__Key_F3] = "F3" + e.specialKeys[core.Qt__Key_F4] = "F4" + e.specialKeys[core.Qt__Key_F5] = "F5" + e.specialKeys[core.Qt__Key_F6] = "F6" + e.specialKeys[core.Qt__Key_F7] = "F7" + e.specialKeys[core.Qt__Key_F8] = "F8" + e.specialKeys[core.Qt__Key_F9] = "F9" + e.specialKeys[core.Qt__Key_F10] = "F10" + e.specialKeys[core.Qt__Key_F11] = "F11" + e.specialKeys[core.Qt__Key_F12] = "F12" + e.specialKeys[core.Qt__Key_F13] = "F13" + e.specialKeys[core.Qt__Key_F14] = "F14" + e.specialKeys[core.Qt__Key_F15] = "F15" + e.specialKeys[core.Qt__Key_F16] = "F16" + e.specialKeys[core.Qt__Key_F17] = "F17" + e.specialKeys[core.Qt__Key_F18] = "F18" + e.specialKeys[core.Qt__Key_F19] = "F19" + e.specialKeys[core.Qt__Key_F20] = "F20" + e.specialKeys[core.Qt__Key_F21] = "F21" + e.specialKeys[core.Qt__Key_F22] = "F22" + e.specialKeys[core.Qt__Key_F23] = "F23" + e.specialKeys[core.Qt__Key_F24] = "F24" + e.specialKeys[core.Qt__Key_Backspace] = "BS" + e.specialKeys[core.Qt__Key_Delete] = "Del" + e.specialKeys[core.Qt__Key_Insert] = "Insert" + e.specialKeys[core.Qt__Key_Home] = "Home" + e.specialKeys[core.Qt__Key_End] = "End" + e.specialKeys[core.Qt__Key_PageUp] = "PageUp" + e.specialKeys[core.Qt__Key_PageDown] = "PageDown" + + e.specialKeys[core.Qt__Key_Return] = "Enter" + e.specialKeys[core.Qt__Key_Enter] = "Enter" + e.specialKeys[core.Qt__Key_Tab] = "Tab" + e.specialKeys[core.Qt__Key_Backtab] = "Tab" + e.specialKeys[core.Qt__Key_Escape] = "Esc" + + e.specialKeys[core.Qt__Key_Backslash] = "Bslash" + e.specialKeys[core.Qt__Key_Space] = "Space" + + goos := runtime.GOOS + e.shiftModifier = core.Qt__ShiftModifier + e.altModifier = core.Qt__AltModifier + e.keyAlt = core.Qt__Key_Alt + e.keyShift = core.Qt__Key_Shift + if goos == "darwin" { + e.controlModifier = core.Qt__MetaModifier + e.cmdModifier = core.Qt__ControlModifier + e.metaModifier = core.Qt__AltModifier + e.keyControl = core.Qt__Key_Meta + e.keyCmd = core.Qt__Key_Control + } else { + e.controlModifier = core.Qt__ControlModifier + e.metaModifier = core.Qt__MetaModifier + e.keyControl = core.Qt__Key_Control + if goos == "linux" { + e.cmdModifier = core.Qt__MetaModifier + e.keyCmd = core.Qt__Key_Meta } - editor.close <- true - }() + } +} - screen.updateSize() +func (e *Editor) close() { + e.stopOnce.Do(func() { + close(e.stop) + }) +} - apiInfo, err := editor.nvim.APIInfo() +func (e *Editor) cleanup() { + home, err := homedir.Dir() if err != nil { - fmt.Println("nvim get API info error", err) - app.Quit() return } + sessions := filepath.Join(home, ".gonvim", "sessions") + os.RemoveAll(sessions) + os.MkdirAll(sessions, 0755) - editor.configure() - o := make(map[string]interface{}) - o["rgb"] = true - o["ext_popupmenu"] = true - o["ext_tabline"] = editor.drawTabline - for _, item := range apiInfo { - i, ok := item.(map[string]interface{}) - if !ok { - continue - } - for k, v := range i { - if k != "ui_events" { - continue - } - events, ok := v.([]interface{}) - if !ok { - continue - } - for _, event := range events { - function, ok := event.(map[string]interface{}) - if !ok { - continue - } - name, ok := function["name"] - if !ok { - continue - } - if name == "wildmenu_show" { - o["ext_wildmenu"] = true - } else if name == "cmdline_show" { - o["ext_cmdline"] = true - } else if name == "msg_chunk" { - o["ext_messages"] = true - } - } - } - } - err = editor.nvim.AttachUI(editor.cols, editor.rows, o) - if err != nil { - fmt.Println("nvim attach UI error", err) - app.Quit() + select { + case <-e.stop: return + default: } - editor.nvim.Subscribe("Gui") - editor.nvim.Command("runtime plugin/nvim_gui_shim.vim") - editor.nvim.Command("runtime! ginit.vim") - editor.nvim.Command("let g:gonvim_running=1") - fuzzy.RegisterPlugin(editor.nvim) - tabline.subscribe() - statusline.subscribe() - loc.subscribe() - message.subscribe() - go func() { - <-editor.close - app.Quit() - }() - - window.Show() - popup.widget.Hide() - palette.hide() - loc.widget.Hide() - signature.widget.Hide() - widgets.QApplication_Exec() + for i, ws := range e.workspaces { + sessionPath := filepath.Join(sessions, strconv.Itoa(i)+".vim") + fmt.Println(sessionPath) + fmt.Println(ws.nvim.Command("mksession " + sessionPath)) + fmt.Println("mksession finished") + } } diff --git a/editor/finder.go b/editor/finder.go index 775ecd40..db1171f6 100644 --- a/editor/finder.go +++ b/editor/finder.go @@ -9,6 +9,7 @@ import ( // Finder is a fuzzy finder window type Finder struct { + ws *Workspace } func initFinder() *Finder { @@ -16,21 +17,21 @@ func initFinder() *Finder { } func (f *Finder) hide() { - editor.palette.hide() + f.ws.palette.hide() } func (f *Finder) cursorPos(args []interface{}) { x := reflectToInt(args[0]) - editor.palette.cursorMove(x) + f.ws.palette.cursorMove(x) } func (f *Finder) selectResult(args []interface{}) { selected := reflectToInt(args[0]) - editor.palette.showSelected(selected) + f.ws.palette.showSelected(selected) } func (f *Finder) showPattern(args []interface{}) { - palette := editor.palette + palette := f.ws.palette p := args[0].(string) palette.patternText = p palette.pattern.SetText(palette.patternText) @@ -38,7 +39,7 @@ func (f *Finder) showPattern(args []interface{}) { } func (f *Finder) showResult(args []interface{}) { - palette := editor.palette + palette := f.ws.palette selected := reflectToInt(args[1]) match := [][]int{} for _, i := range args[2].([]interface{}) { @@ -155,7 +156,7 @@ func formatText(text string, matchIndex []int, path bool) string { sort.Ints(matchIndex) color := "" - if editor != nil && editor.matchFg != nil { + if editor.matchFg != nil { color = editor.matchFg.Hex() } diff --git a/editor/font.go b/editor/font.go index 13f276ea..1d1a9a08 100644 --- a/editor/font.go +++ b/editor/font.go @@ -61,7 +61,7 @@ func (f *Font) change(family string, size int) { f.lineHeight = height + f.lineSpace f.ascent = ascent f.shift = int(float64(f.lineSpace)/2 + ascent) - editor.screen.toolTipFont(f) + // editor.screen.toolTipFont(f) } func (f *Font) changeLineSpace(lineSpace int) { diff --git a/editor/locpopup.go b/editor/locpopup.go index 9a356be5..8b75c345 100644 --- a/editor/locpopup.go +++ b/editor/locpopup.go @@ -9,6 +9,7 @@ import ( // Locpopup is the location popup type Locpopup struct { + ws *Workspace mutex sync.Mutex widget *widgets.QWidget typeLabel *widgets.QLabel @@ -46,17 +47,17 @@ func initLocpopup() *Locpopup { } func (l *Locpopup) subscribe() { - if !editor.drawLint { + if !l.ws.drawLint { return } - editor.signal.ConnectLocpopupSignal(func() { + l.ws.signal.ConnectLocpopupSignal(func() { l.updateLocpopup() }) - editor.nvim.RegisterHandler("LocPopup", func(args ...interface{}) { + l.ws.nvim.RegisterHandler("LocPopup", func(args ...interface{}) { l.handle(args) }) - editor.nvim.Subscribe("LocPopup") - editor.nvim.Command(`autocmd CursorMoved,CursorHold,InsertEnter,InsertLeave * call rpcnotify(0, "LocPopup", "update")`) + l.ws.nvim.Subscribe("LocPopup") + l.ws.nvim.Command(`autocmd CursorMoved,CursorHold,InsertEnter,InsertLeave * call rpcnotify(0, "LocPopup", "update")`) } func (l *Locpopup) updateLocpopup() { @@ -96,16 +97,16 @@ func (l *Locpopup) update(args []interface{}) { defer func() { if !shown { l.shown = false - editor.signal.LocpopupSignal() + l.ws.signal.LocpopupSignal() } l.mutex.Unlock() }() - buf, err := editor.nvim.CurrentBuffer() + buf, err := l.ws.nvim.CurrentBuffer() if err != nil { return } buftype := new(string) - err = editor.nvim.BufferOption(buf, "buftype", buftype) + err = l.ws.nvim.BufferOption(buf, "buftype", buftype) if err != nil { return } @@ -114,7 +115,7 @@ func (l *Locpopup) update(args []interface{}) { } mode := new(string) - err = editor.nvim.Call("mode", mode, "") + err = l.ws.nvim.Call("mode", mode, "") if err != nil { return } @@ -122,16 +123,16 @@ func (l *Locpopup) update(args []interface{}) { return } - curWin, err := editor.nvim.CurrentWindow() + curWin, err := l.ws.nvim.CurrentWindow() if err != nil { return } - pos, err := editor.nvim.WindowCursor(curWin) + pos, err := l.ws.nvim.WindowCursor(curWin) if err != nil { return } result := new([]map[string]interface{}) - err = editor.nvim.Call("getloclist", result, "winnr(\"$\")") + err = l.ws.nvim.Call("getloclist", result, "winnr(\"$\")") if err != nil { return } @@ -156,7 +157,7 @@ func (l *Locpopup) update(args []interface{}) { warnings++ } } - editor.statusline.lint.redraw(errors, warnings) + l.ws.statusline.lint.redraw(errors, warnings) if len(locs) == 0 { return } @@ -177,7 +178,7 @@ func (l *Locpopup) update(args []interface{}) { l.typeText = locType l.contentText = text l.shown = shown - editor.signal.LocpopupSignal() + l.ws.signal.LocpopupSignal() } } diff --git a/editor/message.go b/editor/message.go index 4c4af4da..d75c4f71 100644 --- a/editor/message.go +++ b/editor/message.go @@ -11,6 +11,7 @@ import ( // Message isj type Message struct { + ws *Workspace kind string width int widget *widgets.QWidget @@ -21,6 +22,7 @@ type Message struct { // MessageItem is type MessageItem struct { + m *Message active bool kind string text string @@ -42,6 +44,13 @@ func initMessage() *Message { widget.SetLayout(layout) widget.SetStyleSheet("* {background-color: rgba(24, 29, 34, 1); color: rgba(205, 211, 222, 1);}") + m := &Message{ + width: width, + widget: widget, + layout: layout, + expires: 10, + } + items := []*MessageItem{} for i := 0; i < 10; i++ { w := widgets.NewQWidget(nil, 0) @@ -62,23 +71,19 @@ func initMessage() *Message { w.Hide() l.Hide() items = append(items, &MessageItem{ + m: m, label: l, icon: icon, widget: w, }) } widget.Show() - return &Message{ - width: width, - widget: widget, - layout: layout, - items: items, - expires: 10, - } + m.items = items + return m } func (m *Message) subscribe() { - editor.signal.ConnectMessageSignal(func() { + m.ws.signal.ConnectMessageSignal(func() { m.update() }) } @@ -114,8 +119,8 @@ func (m *Message) update() { } func (m *Message) resize() { - m.width = editor.screen.width / 4 - m.widget.Move2(editor.screen.width-m.width-34, 0) + m.width = m.ws.screen.width / 4 + m.widget.Move2(m.ws.screen.width-m.width-34, 0) m.widget.Resize2(m.width+34, 0) for _, item := range m.items { item.label.SetMinimumHeight(0) @@ -164,7 +169,7 @@ func (m *Message) chunk(args []interface{}) { item.hideAt = time.Now().Add(time.Duration(m.expires) * time.Second) time.AfterFunc(time.Duration(m.expires+1)*time.Second, func() { - editor.signal.MessageSignal() + m.ws.signal.MessageSignal() }) item.setKind(m.kind) item.setText(text) @@ -178,7 +183,7 @@ func (i *MessageItem) setText(text string) { label.SetMinimumHeight(0) // label.SetMaximumHeight(0) label.SetText(text) - height := label.HeightForWidth(editor.message.width) + height := label.HeightForWidth(i.m.width) label.SetMinimumHeight(height) label.SetMaximumHeight(height) i.widget.SetMinimumHeight(height) @@ -223,11 +228,11 @@ func (i *MessageItem) setKind(kind string) { switch i.kind { case "emsg": style += "color: rgba(204, 62, 68, 1);" - svgContent := getSvg("fire", newRGBA(204, 62, 68, 1)) + svgContent := i.m.ws.getSvg("fire", newRGBA(204, 62, 68, 1)) i.icon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) default: style += "color: rgba(81, 154, 186, 1);" - svgContent := getSvg("comment", nil) + svgContent := i.m.ws.getSvg("comment", nil) i.icon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } i.label.SetStyleSheet(style) diff --git a/editor/palette.go b/editor/palette.go index 9e5e4464..4dffe3b6 100644 --- a/editor/palette.go +++ b/editor/palette.go @@ -12,6 +12,7 @@ import ( // Palette is the popup for fuzzy finder, cmdline etc type Palette struct { + ws *Workspace hidden bool widget *widgets.QWidget patternText string @@ -36,6 +37,7 @@ type Palette struct { // PaletteResultItem is the result item type PaletteResultItem struct { + p *Palette hidden bool icon *svg.QSvgWidget iconType string @@ -114,6 +116,19 @@ func initPalette() *Palette { mainLayout.AddWidget(patternWidget, 0, 0) mainLayout.AddWidget(resultMainWidget, 0, 0) + palette := &Palette{ + width: width, + widget: widget, + resultWidget: resultWidget, + resultMainWidget: resultMainWidget, + pattern: pattern, + patternPadding: padding, + patternWidget: patternWidget, + scrollCol: scrollCol, + scrollBar: scrollBar, + cursor: cursor, + } + resultItems := []*PaletteResultItem{} max := 30 for i := 0; i < max; i++ { @@ -135,36 +150,27 @@ func initPalette() *Palette { itemLayout.AddWidget(icon) itemLayout.AddWidget(base) resultItem := &PaletteResultItem{ + p: palette, widget: itemWidget, icon: icon, base: base, } resultItems = append(resultItems, resultItem) } - palette := &Palette{ - width: width, - widget: widget, - resultItems: resultItems, - resultWidget: resultWidget, - resultMainWidget: resultMainWidget, - max: max, - pattern: pattern, - patternPadding: padding, - patternWidget: patternWidget, - scrollCol: scrollCol, - scrollBar: scrollBar, - cursor: cursor, - } + palette.max = max + palette.resultItems = resultItems return palette } func (p *Palette) resize() { - x := (editor.screen.width - p.width) / 2 + x := (p.ws.screen.width - p.width) / 2 p.widget.Move2(x, 0) itemHeight := p.resultItems[0].widget.SizeHint().Height() p.itemHeight = itemHeight - p.showTotal = int(float64(editor.screen.height)/float64(itemHeight)*0.5) - 1 - fuzzy.UpdateMax(editor.nvim, p.showTotal) + p.showTotal = int(float64(p.ws.screen.height)/float64(itemHeight)*0.5) - 1 + if p.ws.uiAttached { + fuzzy.UpdateMax(p.ws.nvim, p.showTotal) + } for i := p.showTotal; i < len(p.resultItems); i++ { p.resultItems[i].hide() @@ -193,7 +199,7 @@ func (p *Palette) setPattern(text string) { } func (p *Palette) cursorMove(x int) { - p.cursorX = int(editor.font.defaultFontMetrics.Width(string(p.patternText[:x]))) + p.cursorX = int(p.ws.font.defaultFontMetrics.Width(string(p.patternText[:x]))) p.cursor.Move2(p.cursorX+p.patternPadding, p.patternPadding) } @@ -271,7 +277,7 @@ func (f *PaletteResultItem) setItem(text string, itemType string, match []int) { } func (f *PaletteResultItem) updateIcon() { - svgContent := getSvg(f.iconType, nil) + svgContent := f.p.ws.getSvg(f.iconType, nil) f.icon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } diff --git a/editor/popupmenu.go b/editor/popupmenu.go index f5c132fd..ef8ab5a6 100644 --- a/editor/popupmenu.go +++ b/editor/popupmenu.go @@ -9,6 +9,7 @@ import ( // PopupMenu is the popupmenu type PopupMenu struct { + ws *Workspace widget *widgets.QWidget layout *widgets.QGridLayout items []*PopupItem @@ -113,8 +114,8 @@ func (p *PopupMenu) showItems(args []interface{}) { p.top = 0 popupItems := p.items - itemHeight := editor.font.height + 20 - heightLeft := editor.screen.height - (row+1)*editor.font.lineHeight + itemHeight := p.ws.font.height + 20 + heightLeft := p.ws.screen.height - (row+1)*p.ws.font.lineHeight total := heightLeft / itemHeight if total < p.total { p.showTotal = total @@ -145,8 +146,8 @@ func (p *PopupMenu) showItems(args []interface{}) { } p.widget.Move2( - int(float64(col)*editor.font.truewidth)-popupItems[0].kindLable.Width()-8, - (row+1)*editor.font.lineHeight, + int(float64(col)*p.ws.font.truewidth)-popupItems[0].kindLable.Width()-8, + (row+1)*p.ws.font.lineHeight, ) p.show() } diff --git a/editor/screen.go b/editor/screen.go index ec41e309..f58e60a0 100644 --- a/editor/screen.go +++ b/editor/screen.go @@ -3,7 +3,6 @@ package editor import ( "fmt" "math" - "runtime" "strings" "sync" "time" @@ -32,6 +31,7 @@ type Screen struct { width int height int widget *widgets.QWidget + ws *Workspace wins map[nvim.Window]*Window cursor [2]int lastCursor [2]int @@ -42,24 +42,13 @@ type Screen struct { highlight Highlight curWins map[nvim.Window]*Window queueRedrawArea [4]int - specialKeys map[core.Qt__Key]string paintMutex sync.Mutex redrawMutex sync.Mutex drawSplit bool tooltip *widgets.QLabel - - controlModifier core.Qt__KeyboardModifier - cmdModifier core.Qt__KeyboardModifier - shiftModifier core.Qt__KeyboardModifier - altModifier core.Qt__KeyboardModifier - metaModifier core.Qt__KeyboardModifier - keyControl core.Qt__Key - keyCmd core.Qt__Key - keyAlt core.Qt__Key - keyShift core.Qt__Key } -func initScreenNew(devicePixelRatio float64) *Screen { +func newScreen() *Screen { widget := widgets.NewQWidget(nil, 0) widget.SetContentsMargins(0, 0, 0, 0) widget.SetAttribute(core.Qt__WA_OpaquePaintEvent, true) @@ -81,13 +70,6 @@ func initScreenNew(devicePixelRatio float64) *Screen { tooltip: tooltip, } widget.ConnectPaintEvent(screen.paint) - screen.initSpecialKeys() - widget.ConnectResizeEvent(func(event *gui.QResizeEvent) { - if editor == nil { - return - } - screen.updateSize() - }) widget.ConnectMousePressEvent(screen.mouseEvent) widget.ConnectMouseReleaseEvent(screen.mouseEvent) widget.ConnectMouseMoveEvent(screen.mouseEvent) @@ -108,22 +90,22 @@ func (s *Screen) toolTip(text string) { row := s.cursor[0] col := s.cursor[1] - c := editor.cursorNew - c.x = int(float64(col)*editor.font.truewidth) + s.tooltip.Width() - c.y = row * editor.font.lineHeight + c := s.ws.cursor + c.x = int(float64(col)*s.ws.font.truewidth) + s.tooltip.Width() + c.y = row * s.ws.font.lineHeight c.move() } // InputMethodEvent is func (s *Screen) InputMethodEvent(event *gui.QInputMethodEvent) { if event.CommitString() != "" { - editor.nvim.Input(event.CommitString()) + s.ws.nvim.Input(event.CommitString()) s.tooltip.Hide() } else { preeditString := event.PreeditString() if preeditString == "" { s.tooltip.Hide() - editor.cursorNew.update() + s.ws.cursor.update() } else { s.toolTip(preeditString) } @@ -135,10 +117,10 @@ func (s *Screen) InputMethodQuery(query core.Qt__InputMethodQuery) *core.QVarian qv := core.NewQVariant() if query == core.Qt__ImCursorRectangle { imrect := core.NewQRect() - row := editor.screen.cursor[0] - col := editor.screen.cursor[1] - x := int(float64(col+3) * editor.font.truewidth) - y := (row + 3) * editor.font.lineHeight + row := s.ws.screen.cursor[0] + col := s.ws.screen.cursor[1] + x := int(float64(col+3) * s.ws.font.truewidth) + y := (row + 3) * s.ws.font.lineHeight imrect.SetRect(x, y, 1, 1) return core.NewQVariant33(imrect) } @@ -146,14 +128,11 @@ func (s *Screen) InputMethodQuery(query core.Qt__InputMethodQuery) *core.QVarian } func (s *Screen) paint(vqp *gui.QPaintEvent) { - if editor == nil { - return - } s.paintMutex.Lock() defer s.paintMutex.Unlock() rect := vqp.M_rect() - font := editor.font + font := s.ws.font top := rect.Y() left := rect.X() width := rect.Width() @@ -166,110 +145,40 @@ func (s *Screen) paint(vqp *gui.QPaintEvent) { cols := int(math.Ceil(float64(right)/font.truewidth)) - col p := gui.NewQPainter2(s.widget) - if editor.Background != nil { + if s.ws.background != nil { p.FillRect5( left, top, width, height, - editor.Background.QColor(), + s.ws.background.QColor(), ) } - p.SetFont(editor.font.fontNew) + p.SetFont(font.fontNew) for y := row; y < row+rows; y++ { - if y >= editor.rows { + if y >= s.ws.rows { continue } - fillHightlight(p, y, col, cols, [2]int{0, 0}) - drawText(p, y, col, cols, [2]int{0, 0}) + s.fillHightlight(p, y, col, cols, [2]int{0, 0}) + s.drawText(p, y, col, cols, [2]int{0, 0}) } s.drawBorder(p, row, col, rows, cols) p.DestroyQPainter() } -func (s *Screen) initSpecialKeys() { - s.specialKeys = map[core.Qt__Key]string{} - s.specialKeys[core.Qt__Key_Up] = "Up" - s.specialKeys[core.Qt__Key_Down] = "Down" - s.specialKeys[core.Qt__Key_Left] = "Left" - s.specialKeys[core.Qt__Key_Right] = "Right" - - s.specialKeys[core.Qt__Key_F1] = "F1" - s.specialKeys[core.Qt__Key_F2] = "F2" - s.specialKeys[core.Qt__Key_F3] = "F3" - s.specialKeys[core.Qt__Key_F4] = "F4" - s.specialKeys[core.Qt__Key_F5] = "F5" - s.specialKeys[core.Qt__Key_F6] = "F6" - s.specialKeys[core.Qt__Key_F7] = "F7" - s.specialKeys[core.Qt__Key_F8] = "F8" - s.specialKeys[core.Qt__Key_F9] = "F9" - s.specialKeys[core.Qt__Key_F10] = "F10" - s.specialKeys[core.Qt__Key_F11] = "F11" - s.specialKeys[core.Qt__Key_F12] = "F12" - s.specialKeys[core.Qt__Key_F13] = "F13" - s.specialKeys[core.Qt__Key_F14] = "F14" - s.specialKeys[core.Qt__Key_F15] = "F15" - s.specialKeys[core.Qt__Key_F16] = "F16" - s.specialKeys[core.Qt__Key_F17] = "F17" - s.specialKeys[core.Qt__Key_F18] = "F18" - s.specialKeys[core.Qt__Key_F19] = "F19" - s.specialKeys[core.Qt__Key_F20] = "F20" - s.specialKeys[core.Qt__Key_F21] = "F21" - s.specialKeys[core.Qt__Key_F22] = "F22" - s.specialKeys[core.Qt__Key_F23] = "F23" - s.specialKeys[core.Qt__Key_F24] = "F24" - s.specialKeys[core.Qt__Key_Backspace] = "BS" - s.specialKeys[core.Qt__Key_Delete] = "Del" - s.specialKeys[core.Qt__Key_Insert] = "Insert" - s.specialKeys[core.Qt__Key_Home] = "Home" - s.specialKeys[core.Qt__Key_End] = "End" - s.specialKeys[core.Qt__Key_PageUp] = "PageUp" - s.specialKeys[core.Qt__Key_PageDown] = "PageDown" - - s.specialKeys[core.Qt__Key_Return] = "Enter" - s.specialKeys[core.Qt__Key_Enter] = "Enter" - s.specialKeys[core.Qt__Key_Tab] = "Tab" - s.specialKeys[core.Qt__Key_Backtab] = "Tab" - s.specialKeys[core.Qt__Key_Escape] = "Esc" - - s.specialKeys[core.Qt__Key_Backslash] = "Bslash" - s.specialKeys[core.Qt__Key_Space] = "Space" - - goos := runtime.GOOS - s.shiftModifier = core.Qt__ShiftModifier - s.altModifier = core.Qt__AltModifier - s.keyAlt = core.Qt__Key_Alt - s.keyShift = core.Qt__Key_Shift - if goos == "darwin" { - s.controlModifier = core.Qt__MetaModifier - s.cmdModifier = core.Qt__ControlModifier - s.metaModifier = core.Qt__AltModifier - s.keyControl = core.Qt__Key_Meta - s.keyCmd = core.Qt__Key_Control - } else { - s.controlModifier = core.Qt__ControlModifier - s.metaModifier = core.Qt__MetaModifier - s.keyControl = core.Qt__Key_Control - if goos == "linux" { - s.cmdModifier = core.Qt__MetaModifier - s.keyCmd = core.Qt__Key_Meta - } - } -} - func (s *Screen) mouseEvent(event *gui.QMouseEvent) { inp := s.convertMouse(event) if inp == "" { return } - editor.nvim.Input(inp) + s.ws.nvim.Input(inp) } func (s *Screen) convertMouse(event *gui.QMouseEvent) string { - font := editor.font + font := s.ws.font x := int(float64(event.X()) / font.truewidth) y := int(float64(event.Y()) / float64(font.lineHeight)) pos := []int{x, y} @@ -315,129 +224,7 @@ func (s *Screen) convertMouse(event *gui.QMouseEvent) string { return "" } - return fmt.Sprintf("<%s%s%s><%d,%d>", s.modPrefix(mod), buttonName, evType, pos[0], pos[1]) -} - -func (s *Screen) keyPress(event *gui.QKeyEvent) { - if editor == nil { - return - } - input := s.convertKey(event.Text(), event.Key(), event.Modifiers()) - if input != "" { - editor.nvim.Input(input) - } -} - -func (s *Screen) convertKey(text string, key int, mod core.Qt__KeyboardModifier) string { - if mod&core.Qt__KeypadModifier > 0 { - switch core.Qt__Key(key) { - case core.Qt__Key_Home: - return fmt.Sprintf("<%sHome>", s.modPrefix(mod)) - case core.Qt__Key_End: - return fmt.Sprintf("<%sEnd>", s.modPrefix(mod)) - case core.Qt__Key_PageUp: - return fmt.Sprintf("<%sPageUp>", s.modPrefix(mod)) - case core.Qt__Key_PageDown: - return fmt.Sprintf("<%sPageDown>", s.modPrefix(mod)) - case core.Qt__Key_Plus: - return fmt.Sprintf("<%sPlus>", s.modPrefix(mod)) - case core.Qt__Key_Minus: - return fmt.Sprintf("<%sMinus>", s.modPrefix(mod)) - case core.Qt__Key_multiply: - return fmt.Sprintf("<%sMultiply>", s.modPrefix(mod)) - case core.Qt__Key_division: - return fmt.Sprintf("<%sDivide>", s.modPrefix(mod)) - case core.Qt__Key_Enter: - return fmt.Sprintf("<%sEnter>", s.modPrefix(mod)) - case core.Qt__Key_Period: - return fmt.Sprintf("<%sPoint>", s.modPrefix(mod)) - case core.Qt__Key_0: - return fmt.Sprintf("<%s0>", s.modPrefix(mod)) - case core.Qt__Key_1: - return fmt.Sprintf("<%s1>", s.modPrefix(mod)) - case core.Qt__Key_2: - return fmt.Sprintf("<%s2>", s.modPrefix(mod)) - case core.Qt__Key_3: - return fmt.Sprintf("<%s3>", s.modPrefix(mod)) - case core.Qt__Key_4: - return fmt.Sprintf("<%s4>", s.modPrefix(mod)) - case core.Qt__Key_5: - return fmt.Sprintf("<%s5>", s.modPrefix(mod)) - case core.Qt__Key_6: - return fmt.Sprintf("<%s6>", s.modPrefix(mod)) - case core.Qt__Key_7: - return fmt.Sprintf("<%s7>", s.modPrefix(mod)) - case core.Qt__Key_8: - return fmt.Sprintf("<%s8>", s.modPrefix(mod)) - case core.Qt__Key_9: - return fmt.Sprintf("<%s9>", s.modPrefix(mod)) - } - } - - if text == "<" { - return "" - } - - specialKey, ok := s.specialKeys[core.Qt__Key(key)] - if ok { - return fmt.Sprintf("<%s%s>", s.modPrefix(mod), specialKey) - } - - if text == "\\" { - return fmt.Sprintf("<%s%s>", s.modPrefix(mod), "Bslash") - } - - c := "" - if mod&s.controlModifier > 0 || mod&s.cmdModifier > 0 { - if int(s.keyControl) == key || int(s.keyCmd) == key || int(s.keyAlt) == key || int(s.keyShift) == key { - return "" - } - c = string(key) - if !(mod&s.shiftModifier > 0) { - c = strings.ToLower(c) - } - } else { - c = text - } - - if c == "" { - return "" - } - - char := core.NewQChar10(c) - if char.Unicode() < 0x100 && !char.IsNumber() && char.IsPrint() { - mod &= ^s.shiftModifier - } - - prefix := s.modPrefix(mod) - if prefix != "" { - return fmt.Sprintf("<%s%s>", prefix, c) - } - - return c -} - -func (s *Screen) modPrefix(mod core.Qt__KeyboardModifier) string { - prefix := "" - if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - if mod&s.cmdModifier > 0 { - prefix += "D-" - } - } - - if mod&s.controlModifier > 0 { - prefix += "C-" - } - - if mod&s.shiftModifier > 0 { - prefix += "S-" - } - - if mod&s.altModifier > 0 { - prefix += "A-" - } - - return prefix + return fmt.Sprintf("<%s%s%s><%d,%d>", editor.modPrefix(mod), buttonName, evType, pos[0], pos[1]) } func (s *Screen) drawBorder(p *gui.QPainter, row, col, rows, cols int) { @@ -458,13 +245,13 @@ func (s *Screen) drawBorder(p *gui.QPainter, row, col, rows, cols int) { continue } - win.drawBorder(p) + win.drawBorder(p, s) } } func (s *Screen) getWindows() { wins := map[nvim.Window]*Window{} - neovim := editor.nvim + neovim := s.ws.nvim curtab, _ := neovim.CurrentTabpage() s.curtab = curtab nwins, _ := neovim.TabpageWindows(curtab) @@ -486,7 +273,7 @@ func (s *Screen) getWindows() { } s.curWins = wins for _, win := range s.curWins { - if win.height+win.pos[0] < editor.rows-s.cmdheight { + if win.height+win.pos[0] < s.ws.rows-s.cmdheight { win.statusline = true } else { win.statusline = false @@ -520,10 +307,10 @@ func (s *Screen) getWindows() { func (s *Screen) updateBg(args []interface{}) { color := reflectToInt(args[0]) if color == -1 { - editor.Background = newRGBA(0, 0, 0, 1) + s.ws.background = newRGBA(0, 0, 0, 1) } else { bg := calcColor(reflectToInt(args[0])) - editor.Background = bg + s.ws.background = bg } } @@ -532,19 +319,12 @@ func (s *Screen) size() (int, int) { return geo.Width(), geo.Height() } -func (s *Screen) updateSize() { - s.width, s.height = s.size() - editor.nvimResize() - editor.palette.resize() - editor.message.resize() -} - func (s *Screen) resize(args []interface{}) { s.cursor[0] = 0 s.cursor[1] = 0 - s.content = make([][]*Char, editor.rows) - for i := 0; i < editor.rows; i++ { - s.content[i] = make([]*Char, editor.cols) + s.content = make([][]*Char, s.ws.rows) + for i := 0; i < s.ws.rows; i++ { + s.content[i] = make([]*Char, s.ws.cols) } s.queueRedrawAll() } @@ -552,9 +332,9 @@ func (s *Screen) resize(args []interface{}) { func (s *Screen) clear(args []interface{}) { s.cursor[0] = 0 s.cursor[1] = 0 - s.content = make([][]*Char, editor.rows) - for i := 0; i < editor.rows; i++ { - s.content[i] = make([]*Char, editor.cols) + s.content = make([][]*Char, s.ws.rows) + for i := 0; i < s.ws.rows; i++ { + s.content[i] = make([]*Char, s.ws.cols) } s.queueRedrawAll() } @@ -562,7 +342,7 @@ func (s *Screen) clear(args []interface{}) { func (s *Screen) eolClear(args []interface{}) { row := s.cursor[0] col := s.cursor[1] - if row >= editor.rows { + if row >= s.ws.rows { return } line := s.content[row] @@ -586,7 +366,7 @@ func (s *Screen) put(args []interface{}) { y := s.cursor[0] row := s.cursor[0] col := s.cursor[1] - if row >= editor.rows { + if row >= s.ws.rows { return } line := s.content[row] @@ -614,7 +394,7 @@ func (s *Screen) put(args []interface{}) { line[col] = char } char.char = c.(string) - char.normalWidth = isNormalWidth(char.char) + char.normalWidth = s.isNormalWidth(char.char) lastChar = char char.highlight = s.highlight col++ @@ -661,7 +441,7 @@ func (s *Screen) highlightSet(args []interface{}) { rgba := calcColor(reflectToInt(fg)) highlight.foreground = rgba } else { - highlight.foreground = editor.Foreground + highlight.foreground = s.ws.foreground } bg, ok := hl["background"] @@ -669,7 +449,7 @@ func (s *Screen) highlightSet(args []interface{}) { rgba := calcColor(reflectToInt(bg)) highlight.background = rgba } else { - highlight.background = editor.Background + highlight.background = s.ws.background } s.highlight = highlight } @@ -696,9 +476,9 @@ func (s *Screen) scroll(args []interface{}) { if top == 0 && bot == 0 && left == 0 && right == 0 { top = 0 - bot = editor.rows - 1 + bot = s.ws.rows - 1 left = 0 - right = editor.cols - 1 + right = s.ws.cols - 1 } s.queueRedraw(left, top, (right - left + 1), (bot - top + 1)) @@ -730,7 +510,7 @@ func (s *Screen) scroll(args []interface{}) { } } s.queueRedraw(left, top, (right - left), -count) - if bot < editor.rows-1 { + if bot < s.ws.rows-1 { s.queueRedraw(left, bot+1, (right - left), -count) } } @@ -744,20 +524,20 @@ func (s *Screen) update() { if width > 0 && height > 0 { // s.item.SetPixmap(s.pixmap) s.widget.Update2( - int(float64(x)*editor.font.truewidth), - y*editor.font.lineHeight, - int(float64(width)*editor.font.truewidth), - height*editor.font.lineHeight, + int(float64(x)*s.ws.font.truewidth), + y*s.ws.font.lineHeight, + int(float64(width)*s.ws.font.truewidth), + height*s.ws.font.lineHeight, ) } - s.queueRedrawArea[0] = editor.cols - s.queueRedrawArea[1] = editor.rows + s.queueRedrawArea[0] = s.ws.cols + s.queueRedrawArea[1] = s.ws.rows s.queueRedrawArea[2] = 0 s.queueRedrawArea[3] = 0 } func (s *Screen) queueRedrawAll() { - s.queueRedrawArea = [4]int{0, 0, editor.cols, editor.rows} + s.queueRedrawArea = [4]int{0, 0, s.ws.cols, s.ws.rows} } func (s *Screen) queueRedraw(x, y, width, height int) { @@ -788,9 +568,9 @@ func (s *Screen) cursorWin() *Window { return s.posWin(s.cursor[1], s.cursor[0]) } -func fillHightlight(p *gui.QPainter, y int, col int, cols int, pos [2]int) { +func (s *Screen) fillHightlight(p *gui.QPainter, y int, col int, cols int, pos [2]int) { rectF := core.NewQRectF() - screen := editor.screen + screen := s.ws.screen if y >= len(screen.content) { return } @@ -824,10 +604,10 @@ func fillHightlight(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } else { // last bg is different; draw the previous and start a new one rectF.SetRect( - float64(start-pos[1])*editor.font.truewidth, - float64((y-pos[0])*editor.font.lineHeight), - float64(end-start+1)*editor.font.truewidth, - float64(editor.font.lineHeight), + float64(start-pos[1])*s.ws.font.truewidth, + float64((y-pos[0])*s.ws.font.lineHeight), + float64(end-start+1)*s.ws.font.truewidth, + float64(s.ws.font.lineHeight), ) p.FillRect4( rectF, @@ -843,10 +623,10 @@ func fillHightlight(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } else { if lastBg != nil { rectF.SetRect( - float64(start-pos[1])*editor.font.truewidth, - float64((y-pos[0])*editor.font.lineHeight), - float64(end-start+1)*editor.font.truewidth, - float64(editor.font.lineHeight), + float64(start-pos[1])*s.ws.font.truewidth, + float64((y-pos[0])*s.ws.font.lineHeight), + float64(end-start+1)*s.ws.font.truewidth, + float64(s.ws.font.lineHeight), ) p.FillRect4( rectF, @@ -863,10 +643,10 @@ func fillHightlight(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } if lastBg != nil { rectF.SetRect( - float64(start-pos[1])*editor.font.truewidth, - float64((y-pos[0])*editor.font.lineHeight), - float64(end-start+1)*editor.font.truewidth, - float64(editor.font.lineHeight), + float64(start-pos[1])*s.ws.font.truewidth, + float64((y-pos[0])*s.ws.font.lineHeight), + float64(end-start+1)*s.ws.font.truewidth, + float64(s.ws.font.lineHeight), ) p.FillRect4( rectF, @@ -875,8 +655,8 @@ func fillHightlight(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } } -func drawText(p *gui.QPainter, y int, col int, cols int, pos [2]int) { - screen := editor.screen +func (s *Screen) drawText(p *gui.QPainter, y int, col int, cols int, pos [2]int) { + screen := s.ws.screen if y >= len(screen.content) { return } @@ -893,7 +673,7 @@ func drawText(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } } } - if col+cols < editor.cols { + if col+cols < s.ws.cols { } for x := col; x < col+cols; x++ { if x >= len(line) { @@ -915,7 +695,7 @@ func drawText(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } fg := char.highlight.foreground if fg == nil { - fg = editor.Foreground + fg = s.ws.foreground } colorSlice, ok := chars[fg] if !ok { @@ -944,8 +724,8 @@ func drawText(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } if text != "" { p.SetPen2(gui.NewQColor3(fg.R, fg.G, fg.B, int(fg.A*255))) - pointF.SetX(float64(col-pos[1]) * editor.font.truewidth) - pointF.SetY(float64((y-pos[0])*editor.font.lineHeight + editor.font.shift)) + pointF.SetX(float64(col-pos[1]) * s.ws.font.truewidth) + pointF.SetY(float64((y-pos[0])*s.ws.font.lineHeight + s.ws.font.shift)) p.DrawText(pointF, text) } } @@ -957,17 +737,17 @@ func drawText(p *gui.QPainter, y int, col int, cols int, pos [2]int) { } fg := char.highlight.foreground if fg == nil { - fg = editor.Foreground + fg = s.ws.foreground } p.SetPen2(gui.NewQColor3(fg.R, fg.G, fg.B, int(fg.A*255))) - pointF.SetX(float64(x-pos[1]) * editor.font.truewidth) - pointF.SetY(float64((y-pos[0])*editor.font.lineHeight + editor.font.shift)) + pointF.SetX(float64(x-pos[1]) * s.ws.font.truewidth) + pointF.SetY(float64((y-pos[0])*s.ws.font.lineHeight + s.ws.font.shift)) p.DrawText(pointF, char.char) } } -func (w *Window) drawBorder(p *gui.QPainter) { - bg := editor.Background +func (w *Window) drawBorder(p *gui.QPainter, s *Screen) { + bg := s.ws.background if w.bg != nil { bg = w.bg } @@ -979,34 +759,34 @@ func (w *Window) drawBorder(p *gui.QPainter) { height++ } p.FillRect5( - int(float64(w.pos[1]+w.width)*editor.font.truewidth), - w.pos[0]*editor.font.lineHeight, - int(editor.font.truewidth), - height*editor.font.lineHeight, + int(float64(w.pos[1]+w.width)*s.ws.font.truewidth), + w.pos[0]*s.ws.font.lineHeight, + int(s.ws.font.truewidth), + height*s.ws.font.lineHeight, gui.NewQColor3(bg.R, bg.G, bg.B, 255), ) p.FillRect5( - int(float64(w.pos[1]+1+w.width)*editor.font.truewidth-1), - w.pos[0]*editor.font.lineHeight, + int(float64(w.pos[1]+1+w.width)*s.ws.font.truewidth-1), + w.pos[0]*s.ws.font.lineHeight, 1, - height*editor.font.lineHeight, + height*s.ws.font.lineHeight, gui.NewQColor3(0, 0, 0, 255), ) gradient := gui.NewQLinearGradient3( - (float64(w.width+w.pos[1])+1)*float64(editor.font.truewidth), + (float64(w.width+w.pos[1])+1)*float64(s.ws.font.truewidth), 0, - (float64(w.width+w.pos[1])+1)*float64(editor.font.truewidth)-6, + (float64(w.width+w.pos[1])+1)*float64(s.ws.font.truewidth)-6, 0, ) gradient.SetColorAt(0, gui.NewQColor3(10, 10, 10, 125)) gradient.SetColorAt(1, gui.NewQColor3(10, 10, 10, 0)) brush := gui.NewQBrush10(gradient) p.FillRect2( - int((float64(w.width+w.pos[1])+1)*editor.font.truewidth)-6, - w.pos[0]*editor.font.lineHeight, + int((float64(w.width+w.pos[1])+1)*s.ws.font.truewidth)-6, + w.pos[0]*s.ws.font.lineHeight, 6, - height*editor.font.lineHeight, + height*s.ws.font.lineHeight, brush, ) @@ -1020,27 +800,37 @@ func (w *Window) drawBorder(p *gui.QPainter) { if w.pos[0] > 0 { p.FillRect5( - int(float64(w.pos[1])*editor.font.truewidth), - w.pos[0]*editor.font.lineHeight-1, - int(float64(w.width+1)*editor.font.truewidth), + int(float64(w.pos[1])*s.ws.font.truewidth), + w.pos[0]*s.ws.font.lineHeight-1, + int(float64(w.width+1)*s.ws.font.truewidth), 1, gui.NewQColor3(0, 0, 0, 255), ) } gradient = gui.NewQLinearGradient3( - float64(w.pos[1])*editor.font.truewidth, - float64(w.pos[0]*editor.font.lineHeight), - float64(w.pos[1])*editor.font.truewidth, - float64(w.pos[0]*editor.font.lineHeight+5), + float64(w.pos[1])*s.ws.font.truewidth, + float64(w.pos[0]*s.ws.font.lineHeight), + float64(w.pos[1])*s.ws.font.truewidth, + float64(w.pos[0]*s.ws.font.lineHeight+5), ) gradient.SetColorAt(0, gui.NewQColor3(10, 10, 10, 125)) gradient.SetColorAt(1, gui.NewQColor3(10, 10, 10, 0)) brush = gui.NewQBrush10(gradient) p.FillRect2( - int(float64(w.pos[1])*editor.font.truewidth), - w.pos[0]*editor.font.lineHeight, - int(float64(w.width+1)*editor.font.truewidth), + int(float64(w.pos[1])*s.ws.font.truewidth), + w.pos[0]*s.ws.font.lineHeight, + int(float64(w.width+1)*s.ws.font.truewidth), 5, brush, ) } + +func (s *Screen) isNormalWidth(char string) bool { + if len(char) == 0 { + return true + } + if char[0] <= 127 { + return true + } + return s.ws.font.fontMetrics.Width(char) == s.ws.font.truewidth +} diff --git a/editor/signature.go b/editor/signature.go index 29e600e1..980d25ab 100644 --- a/editor/signature.go +++ b/editor/signature.go @@ -9,6 +9,7 @@ import ( // Signature is type Signature struct { + ws *Workspace cusor []int comma int formattedText string @@ -99,15 +100,15 @@ func (s *Signature) update() { func (s *Signature) move() { text := s.text - row := editor.screen.cursor[0] + s.cusor[0] - col := editor.screen.cursor[1] + s.cusor[1] + row := s.ws.screen.cursor[0] + s.cusor[0] + col := s.ws.screen.cursor[1] + s.cusor[1] i := strings.Index(text, "(") - x := float64(col) * editor.font.truewidth + x := float64(col) * s.ws.font.truewidth if i > -1 { - x -= editor.font.defaultFontMetrics.Width(string(text[:i])) + x -= s.ws.font.defaultFontMetrics.Width(string(text[:i])) } s.x = int(x) - s.y = row*editor.font.lineHeight - s.height + s.y = row*s.ws.font.lineHeight - s.height s.widget.Move2(s.x, s.y) } diff --git a/editor/statusline.go b/editor/statusline.go index 73d0ee86..54017855 100644 --- a/editor/statusline.go +++ b/editor/statusline.go @@ -14,6 +14,7 @@ import ( // Statusline is type Statusline struct { + ws *Workspace widget *widgets.QWidget bg *RGBA @@ -34,6 +35,7 @@ type Statusline struct { // StatuslineLint is type StatuslineLint struct { + s *Statusline errors int warnings int widget *widgets.QWidget @@ -48,6 +50,7 @@ type StatuslineLint struct { // StatuslineFile is type StatuslineFile struct { + s *Statusline file string fileType string widget *widgets.QWidget @@ -74,6 +77,7 @@ type StatuslinePos struct { // StatusMode is type StatusMode struct { + s *Statusline label *widgets.QLabel mode string text string @@ -82,6 +86,7 @@ type StatusMode struct { // StatuslineGit is type StatuslineGit struct { + s *Statusline branch string file string widget *widgets.QWidget @@ -113,6 +118,11 @@ func initStatuslineNew() *Statusline { } `) + s := &Statusline{ + widget: widget, + updates: make(chan []interface{}, 1000), + } + modeLabel := widgets.NewQLabel(nil, 0) modeLabel.SetContentsMargins(4, 1, 4, 1) modeLayout := widgets.NewQHBoxLayout() @@ -122,8 +132,10 @@ func initStatuslineNew() *Statusline { modeWidget.SetContentsMargins(0, 4, 0, 4) modeWidget.SetLayout(modeLayout) mode := &StatusMode{ + s: s, label: modeLabel, } + s.mode = mode gitIcon := svg.NewQSvgWidget(nil) gitIcon.SetFixedSize2(14, 14) @@ -139,10 +151,12 @@ func initStatuslineNew() *Statusline { gitWidget.SetLayout(gitLayout) gitWidget.Hide() git := &StatuslineGit{ + s: s, widget: gitWidget, icon: gitIcon, label: gitLabel, } + s.git = git fileIcon := svg.NewQSvgWidget(nil) fileIcon.SetFixedSize2(14, 14) @@ -162,29 +176,34 @@ func initStatuslineNew() *Statusline { fileWidget.SetContentsMargins(0, 0, 0, 0) fileWidget.SetLayout(fileLayout) file := &StatuslineFile{ + s: s, icon: fileIcon, widget: fileWidget, fileLabel: fileLabel, folderLabel: folderLabel, } + s.file = file encodingLabel := widgets.NewQLabel(nil, 0) encodingLabel.SetContentsMargins(0, 0, 0, 0) encoding := &StatuslineEncoding{ label: encodingLabel, } + s.encoding = encoding posLabel := widgets.NewQLabel(nil, 0) posLabel.SetContentsMargins(0, 0, 0, 0) pos := &StatuslinePos{ label: posLabel, } + s.pos = pos filetypeLabel := widgets.NewQLabel(nil, 0) filetypeLabel.SetContentsMargins(0, 0, 0, 0) filetype := &StatuslineFiletype{ label: filetypeLabel, } + s.filetype = filetype okIcon := svg.NewQSvgWidget(nil) okIcon.SetFixedSize2(14, 14) @@ -215,6 +234,7 @@ func initStatuslineNew() *Statusline { lintWidget.SetContentsMargins(0, 0, 0, 0) lintWidget.SetLayout(lintLayout) lint := &StatuslineLint{ + s: s, widget: lintWidget, okIcon: okIcon, errorIcon: errorIcon, @@ -225,6 +245,7 @@ func initStatuslineNew() *Statusline { errors: -1, warnings: -1, } + s.lint = lint layout.AddWidget(modeWidget) layout.AddWidget(gitWidget) @@ -234,41 +255,31 @@ func initStatuslineNew() *Statusline { layout.AddWidget(posLabel) layout.AddWidget(lintWidget) - return &Statusline{ - widget: widget, - mode: mode, - git: git, - file: file, - lint: lint, - filetype: filetype, - encoding: encoding, - pos: pos, - updates: make(chan []interface{}, 1000), - } + return s } func (s *Statusline) subscribe() { - if !editor.drawStatusline { + if !s.ws.drawStatusline { s.widget.Hide() return } - editor.signal.ConnectStatuslineSignal(func() { + s.ws.signal.ConnectStatuslineSignal(func() { updates := <-s.updates s.handleUpdates(updates) }) - editor.signal.ConnectLintSignal(func() { + s.ws.signal.ConnectLintSignal(func() { s.lint.update() }) - editor.signal.ConnectGitSignal(func() { + s.ws.signal.ConnectGitSignal(func() { s.git.update() }) - editor.nvim.RegisterHandler("statusline", func(updates ...interface{}) { + s.ws.nvim.RegisterHandler("statusline", func(updates ...interface{}) { s.updates <- updates - editor.signal.StatuslineSignal() + s.ws.signal.StatuslineSignal() }) - editor.nvim.Subscribe("statusline") - editor.nvim.Command(`autocmd BufEnter * call rpcnotify(0, "statusline", "bufenter", expand("%:p"), &filetype, &fileencoding)`) - editor.nvim.Command(`autocmd CursorMoved,CursorMovedI * call rpcnotify(0, "statusline", "cursormoved", getpos("."))`) + s.ws.nvim.Subscribe("statusline") + s.ws.nvim.Command(`autocmd BufEnter * call rpcnotify(0, "statusline", "bufenter", expand("%:p"), &filetype, &fileencoding)`) + s.ws.nvim.Command(`autocmd CursorMoved,CursorMovedI * call rpcnotify(0, "statusline", "cursormoved", getpos("."))`) } func (s *Statusline) handleUpdates(updates []interface{}) { @@ -298,10 +309,10 @@ func (s *StatusMode) update() { } func (s *StatusMode) redraw() { - if editor.mode == s.mode { + if s.s.ws.mode == s.mode { return } - s.mode = editor.mode + s.mode = s.s.ws.mode text := s.mode bg := newRGBA(102, 153, 204, 1) switch s.mode { @@ -328,7 +339,7 @@ func (s *StatuslineGit) hide() { return } s.hidden = true - editor.signal.GitSignal() + s.s.ws.signal.GitSignal() } func (s *StatuslineGit) update() { @@ -339,7 +350,7 @@ func (s *StatuslineGit) update() { s.label.SetText(s.branch) if !s.svgLoaded { s.svgLoaded = true - svgContent := getSvg("git", newRGBA(212, 215, 214, 1)) + svgContent := s.s.ws.getSvg("git", newRGBA(212, 215, 214, 1)) s.icon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } s.widget.Show() @@ -384,12 +395,12 @@ func (s *StatuslineGit) redraw(file string) { if s.branch != branch { s.branch = branch s.hidden = false - editor.signal.GitSignal() + s.s.ws.signal.GitSignal() } } func (s *StatuslineFile) updateIcon() { - svgContent := getSvg(s.fileType, nil) + svgContent := s.s.ws.getSvg(s.fileType, nil) s.icon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } @@ -458,11 +469,11 @@ func (s *StatuslineFiletype) redraw(filetype string) { func (s *StatuslineLint) update() { if !s.svgLoaded { s.svgLoaded = true - svgContent := getSvg("check", newRGBA(141, 193, 73, 1)) + svgContent := s.s.ws.getSvg("check", newRGBA(141, 193, 73, 1)) s.okIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) - svgContent = getSvg("cross", newRGBA(204, 62, 68, 1)) + svgContent = s.s.ws.getSvg("cross", newRGBA(204, 62, 68, 1)) s.errorIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) - svgContent = getSvg("exclamation", nil) + svgContent = s.s.ws.getSvg("exclamation", nil) s.warnIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } @@ -492,5 +503,5 @@ func (s *StatuslineLint) redraw(errors, warnings int) { } s.errors = errors s.warnings = warnings - editor.signal.LintSignal() + s.s.ws.signal.LintSignal() } diff --git a/editor/svg.go b/editor/svg.go index 7ee8c435..602f5269 100644 --- a/editor/svg.go +++ b/editor/svg.go @@ -2,12 +2,8 @@ package editor import ( "fmt" - "sync" ) -var svgsOnce sync.Once -var svgs map[string]*SvgXML - // SvgXML is type SvgXML struct { xml string @@ -26,127 +22,127 @@ type Svg struct { name string } -func getSvgs() map[string]*SvgXML { - svgsOnce.Do(func() { - initSVGS() +func (w *Workspace) getSvgs() map[string]*SvgXML { + w.svgsOnce.Do(func() { + w.initSVGS() }) - return svgs + return w.svgs } -func getSvg(name string, color *RGBA) string { - if editor == nil { - return "" - } - svgsOnce.Do(func() { - initSVGS() +func (w *Workspace) getSvg(name string, color *RGBA) string { + w.svgsOnce.Do(func() { + w.initSVGS() }) - svg := svgs[name] + svg := w.svgs[name] if svg == nil { - svg = svgs["default"] + svg = w.svgs["default"] } if color == nil { color = svg.color } if color == nil { - color = editor.Foreground + color = w.foreground + } + if color == nil { + color = newRGBA(255, 255, 255, 1) } return fmt.Sprintf(svg.xml, color.Hex()) } -func initSVGS() { +func (w *Workspace) initSVGS() { blue := newRGBA(81, 154, 186, 1) - svgs = map[string]*SvgXML{} - svgs["fire"] = &SvgXML{ + w.svgs = map[string]*SvgXML{} + w.svgs["fire"] = &SvgXML{ width: 1792, height: 1792, xml: ` `, } - svgs["comment"] = &SvgXML{ + w.svgs["comment"] = &SvgXML{ width: 1792, height: 1792, color: blue, xml: ` `, } - svgs["cross"] = &SvgXML{ + w.svgs["cross"] = &SvgXML{ width: 100, height: 100, thickness: 2, xml: ``, } - svgs["default"] = &SvgXML{ + w.svgs["default"] = &SvgXML{ width: 200, height: 200, thickness: 0.5, - color: editor.Foreground, + color: w.foreground, xml: ``, } - svgs["empty"] = &SvgXML{ + w.svgs["empty"] = &SvgXML{ width: 200, height: 200, - color: editor.Foreground, + color: w.foreground, xml: ``, } - svgs["folder"] = &SvgXML{ + w.svgs["folder"] = &SvgXML{ width: 1792, height: 1792, thickness: 0.5, - color: editor.Foreground, + color: w.foreground, xml: ``, } - svgs["git"] = &SvgXML{ + w.svgs["git"] = &SvgXML{ width: 128, height: 128, thickness: 0, - color: editor.Foreground, + color: w.foreground, xml: ``, } - svgs["check"] = &SvgXML{ + w.svgs["check"] = &SvgXML{ width: 1792, height: 1792, thickness: 0, color: newRGBA(141, 193, 73, 1), xml: ``, } - svgs["exclamation"] = &SvgXML{ + w.svgs["exclamation"] = &SvgXML{ width: 1792, height: 1792, thickness: 0, color: newRGBA(203, 203, 65, 1), xml: ``, } - svgs["sh"] = &SvgXML{ + w.svgs["sh"] = &SvgXML{ width: 18, height: 18, thickness: 0, - color: editor.Foreground, + color: w.foreground, xml: ``, } - svgs["py"] = &SvgXML{ + w.svgs["py"] = &SvgXML{ width: 128, height: 128, thickness: 0, color: blue, xml: ``, } - svgs["pyc"] = svgs["py"] - svgs["c"] = &SvgXML{ + w.svgs["pyc"] = w.svgs["py"] + w.svgs["c"] = &SvgXML{ width: 128, height: 128, thickness: 1, color: blue, xml: ``, } - svgs["cpp"] = &SvgXML{ + w.svgs["cpp"] = &SvgXML{ width: 128, height: 128, thickness: 0.5, color: blue, xml: ``, } - svgs["go"] = &SvgXML{ + w.svgs["go"] = &SvgXML{ width: 128, height: 128, thickness: 0, diff --git a/editor/tabline.go b/editor/tabline.go index 9f03a79a..3a4f93c7 100644 --- a/editor/tabline.go +++ b/editor/tabline.go @@ -12,6 +12,7 @@ import ( // Tabline of the editor type Tabline struct { + ws *Workspace widget *widgets.QWidget layout *widgets.QLayout CurrentID int @@ -19,10 +20,12 @@ type Tabline struct { marginDefault int marginTop int marginBottom int + height int } // Tab in the tabline type Tab struct { + t *Tabline widget *widgets.QWidget layout *widgets.QHBoxLayout ID int @@ -38,13 +41,85 @@ type Tab struct { hidden bool } -func (s *Tabline) subscribe() { - if !editor.drawTabline { - s.widget.Hide() +func (t *Tabline) subscribe() { + if !t.ws.drawTabline { + t.widget.Hide() return } } +func newHFlowLayout(spacing int, padding int, paddingTop int, rightIdex int, width int) *widgets.QLayout { + layout := widgets.NewQLayout2() + items := []*widgets.QLayoutItem{} + rect := core.NewQRect() + layout.ConnectSizeHint(func() *core.QSize { + size := core.NewQSize() + for _, item := range items { + size = size.ExpandedTo(item.MinimumSize()) + } + return size + }) + if width > 0 { + layout.ConnectMinimumSize(func() *core.QSize { + size := core.NewQSize() + for _, item := range items { + size = size.ExpandedTo(item.MinimumSize()) + } + if size.Width() > width { + size.SetWidth(width) + } + // size.SetWidth(0) + return size + }) + layout.ConnectMaximumSize(func() *core.QSize { + size := core.NewQSize() + for _, item := range items { + size = size.ExpandedTo(item.MinimumSize()) + } + // size.SetWidth(width) + return size + }) + } + layout.ConnectAddItem(func(item *widgets.QLayoutItem) { + items = append(items, item) + }) + layout.ConnectSetGeometry(func(r *core.QRect) { + sizes := [][]int{} + maxWidth := 0 + for _, item := range items { + sizeHint := item.SizeHint() + width := sizeHint.Width() + height := sizeHint.Height() + size := []int{width, height} + sizes = append(sizes, size) + if width > maxWidth { + maxWidth = width + } + } + y := 0 + for i, item := range items { + size := sizes[i] + height := size[1] + rect.SetRect(0, y, maxWidth, height) + item.SetGeometry(rect) + y += height + } + }) + layout.ConnectItemAt(func(index int) *widgets.QLayoutItem { + if index < len(items) { + return items[index] + } + return nil + }) + layout.ConnectTakeAt(func(index int) *widgets.QLayoutItem { + if index < len(items) { + return items[index] + } + return nil + }) + return layout +} + func newVFlowLayout(spacing int, padding int, paddingTop int, rightIdex int, width int) *widgets.QLayout { layout := widgets.NewQLayout2() items := []*widgets.QLayoutItem{} @@ -141,7 +216,7 @@ func newVFlowLayout(spacing int, padding int, paddingTop int, rightIdex int, wid return layout } -func initTablineNew() *Tabline { +func newTabline() *Tabline { width := 210 widget := widgets.NewQWidget(nil, 0) layout := widgets.NewQLayout2() @@ -191,7 +266,13 @@ func initTablineNew() *Tabline { marginDefault := 10 marginTop := 10 marginBottom := 10 - + tabline := &Tabline{ + widget: widget, + layout: layout, + marginDefault: marginDefault, + marginTop: marginTop, + marginBottom: marginBottom, + } tabs := []*Tab{} for i := 0; i < 10; i++ { w := widgets.NewQWidget(nil, 0) @@ -212,6 +293,7 @@ func initTablineNew() *Tabline { l.AddWidget(closeIcon, 0, 0) w.SetLayout(l) tab := &Tab{ + t: tabline, widget: w, layout: l, file: file, @@ -220,26 +302,22 @@ func initTablineNew() *Tabline { } tabs = append(tabs, tab) layout.AddWidget(w) + if i > 0 { + tab.hide() + } } - - return &Tabline{ - widget: widget, - layout: layout, - Tabs: tabs, - marginDefault: marginDefault, - marginTop: marginTop, - marginBottom: marginBottom, - } + tabline.Tabs = tabs + return tabline } func (t *Tab) updateActive() { if t.active { t.widget.SetStyleSheet(".QWidget {border-bottom: 2px solid rgba(81, 154, 186, 1); background-color: rgba(0, 0, 0, 1); } QWidget{color: rgba(212, 215, 214, 1);} ") - svgContent := getSvg("cross", newRGBA(212, 215, 214, 1)) + svgContent := t.t.ws.getSvg("cross", newRGBA(212, 215, 214, 1)) t.closeIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } else { t.widget.SetStyleSheet("") - svgContent := getSvg("cross", newRGBA(147, 161, 161, 1)) + svgContent := t.t.ws.getSvg("cross", newRGBA(147, 161, 161, 1)) t.closeIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } } @@ -269,18 +347,22 @@ func (t *Tab) setActive(active bool) { } func (t *Tab) updateFileText() { - text := editor.font.defaultFontMetrics.ElidedText(t.fileText, core.Qt__ElideLeft, float64(t.file.Width()), 0) + text := t.t.ws.font.defaultFontMetrics.ElidedText(t.fileText, core.Qt__ElideLeft, float64(t.file.Width()), 0) t.file.SetText(text) } func (t *Tab) updateFileIcon() { - svgContent := getSvg(t.fileType, nil) + svgContent := t.t.ws.getSvg(t.fileType, nil) t.fileIcon.Load2(core.NewQByteArray2(svgContent, len(svgContent))) } func (t *Tabline) updateMargin() { for _, tab := range t.Tabs { tab.file.SetContentsMargins(0, t.marginTop, 0, t.marginBottom) + if !tab.hidden { + tab.hide() + tab.show() + } } } diff --git a/editor/utils.go b/editor/utils.go index f85989f8..328cb62b 100644 --- a/editor/utils.go +++ b/editor/utils.go @@ -1,21 +1,15 @@ package editor -func isNormalWidth(char string) bool { - if len(char) == 0 { - return true - } - if char[0] <= 127 { - return true - } - return editor.font.fontMetrics.Width(char) == editor.font.truewidth -} - func reflectToInt(iface interface{}) int { - o, ok := iface.(int64) + i, ok := iface.(int64) + if ok { + return int(i) + } + u, ok := iface.(uint64) if ok { - return int(o) + return int(u) } - return int(iface.(uint64)) + return 0 } func isZero(d interface{}) bool { diff --git a/editor/workspace.go b/editor/workspace.go new file mode 100644 index 00000000..e6baecfb --- /dev/null +++ b/editor/workspace.go @@ -0,0 +1,698 @@ +package editor + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + + "github.com/dzhou121/gonvim/fuzzy" + "github.com/neovim/go-client/nvim" + "github.com/therecipe/qt/core" + "github.com/therecipe/qt/widgets" +) + +type workspaceSignal struct { + core.QObject + _ func() `signal:"stopSignal"` + _ func() `signal:"redrawSignal"` + _ func() `signal:"guiSignal"` + _ func() `signal:"statuslineSignal"` + _ func() `signal:"locpopupSignal"` + _ func() `signal:"lintSignal"` + _ func() `signal:"gitSignal"` + _ func() `signal:"messageSignal"` +} + +// Workspace is an editor workspace +type Workspace struct { + widget *widgets.QWidget + font *Font + cursor *Cursor + tabline *Tabline + statusline *Statusline + screen *Screen + finder *Finder + palette *Palette + popup *PopupMenu + loc *Locpopup + cmdline *Cmdline + signature *Signature + message *Message + svgs map[string]*SvgXML + svgsOnce sync.Once + width int + height int + hidden bool + + nvim *nvim.Nvim + rows int + cols int + uiAttached bool + foreground *RGBA + background *RGBA + special *RGBA + mode string + cwd string + cwdBase string + + signal *workspaceSignal + redrawUpdates chan [][]interface{} + guiUpdates chan []interface{} + stopOnce sync.Once + stop chan struct{} + + drawStatusline bool + drawTabline bool + drawLint bool +} + +func newWorkspace(path string) (*Workspace, error) { + w := &Workspace{ + stop: make(chan struct{}), + signal: NewWorkspaceSignal(nil), + redrawUpdates: make(chan [][]interface{}, 1000), + guiUpdates: make(chan []interface{}, 1000), + } + w.signal.ConnectRedrawSignal(func() { + updates := <-w.redrawUpdates + w.handleRedraw(updates) + }) + w.signal.ConnectGuiSignal(func() { + updates := <-w.guiUpdates + w.handleRPCGui(updates) + }) + w.signal.ConnectStopSignal(func() { + workspaces := []*Workspace{} + index := 0 + for i, ws := range editor.workspaces { + if ws != w { + workspaces = append(workspaces, ws) + } else { + index = i + } + } + if len(workspaces) == 0 { + editor.close() + return + } + editor.workspaces = workspaces + w.hide() + if editor.active == index { + if index > 0 { + editor.active-- + } + editor.workspaceUpdate() + } + }) + fontFamily := "" + switch runtime.GOOS { + case "windows": + fontFamily = "Consolas" + case "darwin": + fontFamily = "Courier New" + default: + fontFamily = "Monospace" + } + w.font = initFontNew(fontFamily, 14, 6) + w.tabline = newTabline() + w.tabline.ws = w + w.statusline = initStatuslineNew() + w.statusline.ws = w + w.screen = newScreen() + w.screen.toolTipFont(w.font) + w.screen.ws = w + w.cursor = initCursorNew() + w.cursor.widget.SetParent(w.screen.widget) + w.cursor.ws = w + w.popup = initPopupmenuNew(w.font) + w.popup.widget.SetParent(w.screen.widget) + w.popup.ws = w + w.finder = initFinder() + w.finder.ws = w + w.palette = initPalette() + w.palette.widget.SetParent(w.screen.widget) + w.palette.ws = w + w.loc = initLocpopup() + w.loc.widget.SetParent(w.screen.widget) + w.loc.ws = w + w.signature = initSignature() + w.signature.widget.SetParent(w.screen.widget) + w.signature.ws = w + w.message = initMessage() + w.message.widget.SetParent(w.screen.widget) + w.message.ws = w + w.cmdline = initCmdline() + w.cmdline.ws = w + + layout := widgets.NewQVBoxLayout() + w.widget = widgets.NewQWidget(nil, 0) + w.widget.SetContentsMargins(0, 0, 0, 0) + w.widget.SetLayout(layout) + layout.AddWidget(w.tabline.widget, 0, 0) + layout.AddWidget(w.screen.widget, 1, 0) + layout.AddWidget(w.statusline.widget, 0, 0) + layout.SetContentsMargins(0, 0, 0, 0) + layout.SetSpacing(0) + + w.popup.widget.Hide() + w.palette.hide() + w.loc.widget.Hide() + w.signature.widget.Hide() + + w.widget.SetParent(editor.wsWidget) + w.widget.Move2(0, 0) + w.updateSize() + + // err := w.startNvim() + // if err != nil { + // return nil, err + // } + + go w.startNvim(path) + + return w, nil +} + +func (w *Workspace) hide() { + if w.hidden { + return + } + w.hidden = true + w.widget.Hide() +} + +func (w *Workspace) show() { + if !w.hidden { + return + } + w.hidden = false + w.widget.Show() +} + +func (w *Workspace) startNvim(path string) error { + neovim, err := nvim.NewEmbedded(&nvim.EmbedOptions{ + Args: os.Args[1:], + }) + if err != nil { + return err + } + w.nvim = neovim + w.nvim.RegisterHandler("Gui", func(updates ...interface{}) { + w.guiUpdates <- updates + w.signal.GuiSignal() + }) + w.nvim.RegisterHandler("redraw", func(updates ...[]interface{}) { + w.redrawUpdates <- updates + w.signal.RedrawSignal() + }) + go func() { + err := w.nvim.Serve() + if err != nil { + fmt.Println(err) + } + w.stopOnce.Do(func() { + close(w.stop) + }) + w.signal.StopSignal() + }() + + w.configure() + w.attachUI(path) + w.initCwd() + + return nil +} + +func (w *Workspace) configure() { + var drawSplit interface{} + w.nvim.Var("gonvim_draw_split", &drawSplit) + if isZero(drawSplit) { + w.screen.drawSplit = false + } else { + w.screen.drawSplit = true + } + + var drawStatusline interface{} + w.nvim.Var("gonvim_draw_statusline", &drawStatusline) + if isZero(drawStatusline) { + w.drawStatusline = false + } else { + w.drawStatusline = true + } + + var drawTabline interface{} + w.nvim.Var("gonvim_draw_tabline", &drawTabline) + if isZero(drawTabline) { + w.drawTabline = false + } else { + w.drawTabline = true + } + + var drawLint interface{} + w.nvim.Var("gonvim_draw_lint", &drawLint) + if isZero(drawLint) { + w.drawLint = false + } else { + w.drawLint = true + } + + // var startFullscreen interface{} + // w.nvim.Var("gonvim_start_fullscreen", &startFullscreen) + // if isTrue(startFullscreen) { + // e.window.ShowFullScreen() + // } +} + +func (w *Workspace) attachUI(path string) error { + w.nvim.Subscribe("Gui") + w.nvim.Command("runtime plugin/nvim_gui_shim.vim") + w.nvim.Command("runtime! ginit.vim") + w.nvim.Command("let g:gonvim_running=1") + w.workspaceCommands(path) + fuzzy.RegisterPlugin(w.nvim) + w.tabline.subscribe() + w.statusline.subscribe() + w.loc.subscribe() + w.message.subscribe() + w.uiAttached = true + err := w.nvim.AttachUI(w.cols, w.rows, w.attachUIOption()) + if err != nil { + return err + } + return nil +} + +func (w *Workspace) workspaceCommands(path string) { + w.nvim.Command(`autocmd DirChanged * call rpcnotify(0, "Gui", "gonvim_workspace_cwd", getcwd())`) + w.nvim.Command(`command! GonvimWorkspaceNew call rpcnotify(0, 'Gui', 'gonvim_workspace_new')`) + w.nvim.Command(`command! GonvimWorkspaceNext call rpcnotify(0, 'Gui', 'gonvim_workspace_next')`) + w.nvim.Command(`command! GonvimWorkspacePrevious call rpcnotify(0, 'Gui', 'gonvim_workspace_previous')`) + w.nvim.Command(`command! -nargs=1 GonvimWorkspaceSwitch call rpcnotify(0, 'Gui', 'gonvim_workspace_switch', )`) + if path != "" { + w.nvim.Command("so " + path) + } +} + +func (w *Workspace) initCwd() { + cwd := "" + w.nvim.Eval("getcwd()", &cwd) + w.nvim.Command("cd " + cwd) +} + +func (w *Workspace) setCwd(cwd string) { + if cwd == w.cwd { + return + } + w.cwd = cwd + base := filepath.Base(cwd) + w.cwdBase = base + for i, ws := range editor.workspaces { + if i >= len(editor.wsSide.items) { + return + } + if ws == w { + editor.wsSide.items[i].label.SetText(base) + return + } + } +} + +func (w *Workspace) attachUIOption() map[string]interface{} { + o := make(map[string]interface{}) + o["rgb"] = true + + apiInfo, err := w.nvim.APIInfo() + if err == nil { + for _, item := range apiInfo { + i, ok := item.(map[string]interface{}) + if !ok { + continue + } + for k, v := range i { + if k != "ui_events" { + continue + } + events, ok := v.([]interface{}) + if !ok { + continue + } + for _, event := range events { + function, ok := event.(map[string]interface{}) + if !ok { + continue + } + name, ok := function["name"] + if !ok { + continue + } + if name == "wildmenu_show" { + o["ext_wildmenu"] = true + } else if name == "cmdline_show" { + o["ext_cmdline"] = true + } else if name == "msg_chunk" { + o["ext_messages"] = true + } else if name == "popupmenu_show" { + o["ext_popupmenu"] = true + } else if name == "tabline_update" { + o["ext_tabline"] = w.drawTabline + } + } + } + } + } + return o +} + +func (w *Workspace) updateSize() { + e := editor + width := e.wsWidget.Width() + height := e.wsWidget.Height() + if width != w.width || height != w.height { + w.width = width + w.height = height + w.widget.Resize2(width, height) + if !w.hidden { + w.hide() + w.show() + } else { + w.show() + w.hide() + } + } + + if w.tabline.height == 0 { + w.tabline.height = w.tabline.widget.Height() - w.tabline.marginTop - w.tabline.marginBottom + } + + height = height - w.tabline.height - w.tabline.marginDefault*2 - w.statusline.widget.Height() + + cols := int(float64(width) / w.font.truewidth) + rows := height / w.font.lineHeight + + remainingHeight := height - rows*w.font.lineHeight + remainingHeightBottom := remainingHeight / 2 + remainingHeightTop := remainingHeight - remainingHeightBottom + w.tabline.marginTop = w.tabline.marginDefault + remainingHeightTop + w.tabline.marginBottom = w.tabline.marginDefault + remainingHeightBottom + w.tabline.updateMargin() + + if w.uiAttached { + if cols != w.cols || rows != w.rows { + w.nvim.TryResizeUI(cols, rows) + } + } + w.cols = cols + w.rows = rows + + w.screen.width = width + w.screen.height = height - remainingHeight + + w.palette.resize() + w.message.resize() +} + +func (w *Workspace) handleRedraw(updates [][]interface{}) { + s := w.screen + for _, update := range updates { + event := update[0].(string) + args := update[1:] + switch event { + case "update_fg": + args := update[1].([]interface{}) + color := reflectToInt(args[0]) + if color == -1 { + w.foreground = newRGBA(255, 255, 255, 1) + } else { + w.foreground = calcColor(reflectToInt(args[0])) + } + case "update_bg": + args := update[1].([]interface{}) + s.updateBg(args) + case "update_sp": + args := update[1].([]interface{}) + color := reflectToInt(args[0]) + if color == -1 { + w.special = newRGBA(255, 255, 255, 1) + } else { + w.special = calcColor(reflectToInt(args[0])) + } + case "cursor_goto": + s.cursorGoto(args) + case "put": + s.put(args) + case "eol_clear": + s.eolClear(args) + case "clear": + s.clear(args) + case "resize": + s.resize(args) + case "highlight_set": + s.highlightSet(args) + case "set_scroll_region": + s.setScrollRegion(args) + case "scroll": + s.scroll(args) + case "mode_change": + arg := update[len(update)-1].([]interface{}) + w.mode = arg[0].(string) + case "popupmenu_show": + w.popup.showItems(args) + case "popupmenu_hide": + w.popup.hide() + case "popupmenu_select": + w.popup.selectItem(args) + case "tabline_update": + w.tabline.update(args) + case "cmdline_show": + w.cmdline.show(args) + case "cmdline_pos": + w.cmdline.changePos(args) + case "cmdline_char": + w.cmdline.putChar(args) + case "cmdline_hide": + w.cmdline.hide(args) + case "cmdline_function_show": + w.cmdline.functionShow() + case "cmdline_function_hide": + w.cmdline.functionHide() + case "wildmenu_show": + w.cmdline.wildmenuShow(args) + case "wildmenu_select": + w.cmdline.wildmenuSelect(args) + case "wildmenu_hide": + w.cmdline.wildmenuHide() + case "msg_start_kind": + if len(args) > 0 { + kinds, ok := args[len(args)-1].([]interface{}) + if ok { + if len(kinds) > 0 { + kind, ok := kinds[len(kinds)-1].(string) + if ok { + w.message.kind = kind + } + } + } + } + case "msg_chunk": + w.message.chunk(args) + case "msg_end": + case "msg_showcmd": + case "messages": + case "busy_start": + case "busy_stop": + default: + fmt.Println("Unhandle event", event) + } + } + s.update() + w.cursor.update() + w.statusline.mode.redraw() +} + +func (w *Workspace) handleRPCGui(updates []interface{}) { + event := updates[0].(string) + switch event { + case "Font": + w.guiFont(updates[1:]) + case "Linespace": + w.guiLinespace(updates[1:]) + case "finder_pattern": + w.finder.showPattern(updates[1:]) + case "finder_pattern_pos": + w.finder.cursorPos(updates[1:]) + case "finder_show_result": + w.finder.showResult(updates[1:]) + case "finder_hide": + w.finder.hide() + case "finder_select": + w.finder.selectResult(updates[1:]) + case "signature_show": + w.signature.showItem(updates[1:]) + case "signature_pos": + w.signature.pos(updates[1:]) + case "signature_hide": + w.signature.hide() + case "gonvim_workspace_new": + editor.workspaceNew() + case "gonvim_workspace_next": + editor.workspaceNext() + case "gonvim_workspace_previous": + editor.workspacePrevious() + case "gonvim_workspace_switch": + editor.workspaceSwitch(reflectToInt(updates[1])) + case "gonvim_workspace_cwd": + w.setCwd(updates[1].(string)) + default: + fmt.Println("unhandled Gui event", event) + } +} + +func (w *Workspace) guiFont(args ...interface{}) { + fontArg := args[0].([]interface{}) + parts := strings.Split(fontArg[0].(string), ":") + if len(parts) < 1 { + return + } + + 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 + } + } + } + + w.font.change(parts[0], height) + w.updateSize() + w.popup.updateFont(w.font) +} + +func (w *Workspace) guiLinespace(args ...interface{}) { + fontArg := args[0].([]interface{}) + var lineSpace int + var err error + switch arg := fontArg[0].(type) { + case string: + lineSpace, err = strconv.Atoi(arg) + if err != nil { + return + } + case int32: // can't combine these in a type switch without compile error + lineSpace = int(arg) + case int64: + lineSpace = int(arg) + default: + return + } + w.font.changeLineSpace(lineSpace) + w.updateSize() +} + +func (w *Workspace) getInfo() { +} + +// WorkspaceSide is +type WorkspaceSide struct { + widget *widgets.QWidget + items []*WorkspaceSideItem +} + +// WorkspaceSideItem is +type WorkspaceSideItem struct { + hidden bool + active bool + side *WorkspaceSide + label *widgets.QLabel + text string +} + +func newWorkspaceSide() *WorkspaceSide { + layout := newHFlowLayout(0, 0, 0, 0, 20) + layout.SetContentsMargins(0, 0, 0, 0) + layout.SetSpacing(0) + widget := widgets.NewQWidget(nil, 0) + widget.SetContentsMargins(0, 0, 0, 0) + widget.SetLayout(layout) + widget.SetStyleSheet(` + QWidget { + color: rgba(147, 161, 161, 1); + border-right: 1px solid rgba(0, 0, 0, 1); + } + .QWidget { + background-color: rgba(24, 29, 34, 1); + } + `) + + side := &WorkspaceSide{ + widget: widget, + } + + items := []*WorkspaceSideItem{} + for i := 0; i < 20; i++ { + label := widgets.NewQLabel(nil, 0) + label.SetContentsMargins(15, 10, 15, 10) + item := &WorkspaceSideItem{ + side: side, + label: label, + } + layout.AddWidget(label) + items = append(items, item) + item.hide() + } + side.items = items + return side +} + +func (i *WorkspaceSideItem) setText(text string) { + if i.text == text { + return + } + i.text = text + i.label.SetText(text) +} + +func (i *WorkspaceSideItem) setActive() { + if i.active { + return + } + i.active = true + i.label.SetStyleSheet(` + border-left: 3px solid rgba(81, 154, 186, 1); + background-color: rgba(0, 0, 0, 1); + color: rgba(212, 215, 214, 1); + `) +} + +func (i *WorkspaceSideItem) setInactive() { + if !i.active { + return + } + i.active = false + i.label.SetStyleSheet(` + background-color: rgba(24, 29, 34, 1); + color: rgba(147, 161, 161, 1); + `) +} + +func (i *WorkspaceSideItem) show() { + if !i.hidden { + return + } + i.hidden = false + i.label.Show() +} + +func (i *WorkspaceSideItem) hide() { + if i.hidden { + return + } + i.hidden = true + i.label.Hide() +}