Starting custom editor window

This commit is contained in:
bloeys
2022-02-25 09:13:40 +04:00
parent 7f57fcb7e0
commit ceda79cffb
5 changed files with 216 additions and 60 deletions

189
editor.go Executable file
View File

@ -0,0 +1,189 @@
package main
import (
"os"
"path/filepath"
"github.com/bloeys/gopad/settings"
"github.com/inkyblackness/imgui-go/v4"
)
const (
linesPerNode int = 100
)
type Line struct {
chars []rune
}
type LinesNode struct {
Lines [linesPerNode]Line
Next *LinesNode
}
type Editor struct {
FileName string
FilePath string
FileContents string
CursorX int
CursorY int
SelectionStart int
SelectionLength int
IsModified bool
LinesHead *LinesNode
LineCount int
}
func (e *Editor) SetCursorPos(x, y int) {
e.CursorX = x
e.CursorY = y
}
func (e *Editor) Render(drawStartPos, winSize *imgui.Vec2) {
e.CursorY = clampInt(e.CursorY, 0, e.LineCount)
e.CursorX = clampInt(e.CursorX, 0, e.GetLineCharCount(e.CursorY))
//Draw window
imgui.SetNextWindowPos(*drawStartPos)
imgui.SetNextWindowSize(*winSize)
imgui.BeginV("editorText", nil, imgui.WindowFlagsNoCollapse|imgui.WindowFlagsNoDecoration|imgui.WindowFlagsNoMove)
imgui.PushStyleColor(imgui.StyleColorFrameBg, settings.EditorBgColor)
imgui.PushStyleColor(imgui.StyleColorTextSelectedBg, settings.TextSelectionColor)
//Add padding to text
drawStartPos.X += 10
drawStartPos.Y += 10
//Draw lines
lineHeight := imgui.TextLineHeightWithSpacing()
linesToDraw := int(winSize.Y / lineHeight)
println("Lines to draw:", linesToDraw)
dl := imgui.WindowDrawList()
for i := 0; i < linesToDraw; i++ {
dl.AddText(*drawStartPos, imgui.PackedColorFromVec4(imgui.Vec4{X: 1, Y: 1, Z: 1, W: 1}), string(e.GetLine(e.CursorY+i).chars))
drawStartPos.Y += lineHeight
}
}
func (e *Editor) GetLine(lineNum int) *Line {
if lineNum > e.LineCount {
return &Line{
chars: []rune{},
}
}
curr := e.LinesHead
//Advance to correct node
nodeNum := lineNum / linesPerNode
lineNum -= nodeNum * linesPerNode
for nodeNum > 0 {
curr = curr.Next
nodeNum--
}
return &curr.Lines[lineNum]
}
func (e *Editor) GetLineCharCount(lineNum int) int {
curr := e.LinesHead
//Advance to correct node
nodeNum := lineNum / linesPerNode
lineNum -= nodeNum * linesPerNode
for nodeNum > 0 {
curr = curr.Next
nodeNum--
}
return len(curr.Lines[lineNum].chars)
}
func ParseLines(fileContents string) (*LinesNode, int) {
head := NewLineNode()
lineCount := 0
start := 0
end := 0
currLine := 0
currNode := head
for i := 0; i < len(fileContents); i++ {
if fileContents[i] != '\n' {
end++
continue
}
lineCount++
currNode.Lines[currLine].chars = []rune(fileContents[start:end])
end++
start = end
currLine++
if currLine == linesPerNode {
currLine = 0
currNode.Next = NewLineNode()
currNode = currNode.Next
}
}
return head, lineCount
}
func NewLineNode() *LinesNode {
n := LinesNode{}
for i := 0; i < len(n.Lines); i++ {
n.Lines[i].chars = []rune{}
}
return &n
}
func clampInt(x, min, max int) int {
if x > max {
return max
}
if x < min {
return min
}
return x
}
func NewScratchEditor() *Editor {
e := &Editor{
FileName: "**scratch**",
LinesHead: NewLineNode(),
}
return e
}
func NewEditor(fPath string) *Editor {
b, err := os.ReadFile(fPath)
if err != nil {
panic(err)
}
e := &Editor{
FileName: filepath.Base(fPath),
FilePath: fPath,
FileContents: string(b),
}
e.LinesHead, e.LineCount = ParseLines(e.FileContents)
return e
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 34 KiB

79
main.go Normal file → Executable file
View File

@ -16,13 +16,6 @@ import (
//TODO: Cache os.ReadDir so we don't have to use lots of disk //TODO: Cache os.ReadDir so we don't have to use lots of disk
type Editor struct {
fileName string
filePath string
fileContents string
isModified bool
}
type Gopad struct { type Gopad struct {
Win *engine.Window Win *engine.Window
mainFont imgui.Font mainFont imgui.Font
@ -45,10 +38,6 @@ type Gopad struct {
haveErr bool haveErr bool
errMsg string errMsg string
//Settings
textSelectionColor imgui.Vec4
editorBgColor imgui.Vec4
//Cache window size //Cache window size
winWidth float32 winWidth float32
winHeight float32 winHeight float32
@ -79,17 +68,12 @@ func main() {
} }
g := Gopad{ g := Gopad{
Win: window, Win: window,
ImGUIInfo: nmageimgui.NewImGUI(), ImGUIInfo: nmageimgui.NewImGUI(),
CurrDir: dir, CurrDir: dir,
editors: []Editor{ editors: []Editor{*NewScratchEditor()},
{fileName: "**scratch**"}, editorToClose: -1,
},
editorToClose: -1,
sidebarWidthFactor: 0.15, sidebarWidthFactor: 0.15,
textSelectionColor: imgui.Vec4{X: 84 / 255.0, Y: 153 / 255.0, Z: 199 / 255.0, W: 0.4},
editorBgColor: imgui.Vec4{X: 0.1, Y: 0.1, Z: 0.1, W: 1},
} }
// engine.SetVSync(true) // engine.SetVSync(true)
@ -128,9 +112,9 @@ func (g *Gopad) Init() {
} }
g.editors = append(g.editors, Editor{ g.editors = append(g.editors, Editor{
fileName: filepath.Base(os.Args[i]), FileName: filepath.Base(os.Args[i]),
filePath: os.Args[i], FilePath: os.Args[i],
fileContents: string(b), FileContents: string(b),
}) })
} }
@ -190,7 +174,7 @@ func (g *Gopad) Update() {
//Save if needed //Save if needed
e := g.getActiveEditor() e := g.getActiveEditor()
if !e.isModified { if !e.IsModified {
return return
} }
@ -203,18 +187,18 @@ func (g *Gopad) Update() {
func (g *Gopad) saveEditor(e *Editor) { func (g *Gopad) saveEditor(e *Editor) {
if e.fileName == "**scratch**" { if e.FileName == "**scratch**" {
e.isModified = false e.IsModified = false
return return
} }
err := os.WriteFile(e.filePath, []byte(e.fileContents), os.ModePerm) err := os.WriteFile(e.FilePath, []byte(e.FileContents), os.ModePerm)
if err != nil { if err != nil {
g.triggerError("Failed to save file. Error: " + err.Error()) g.triggerError("Failed to save file. Error: " + err.Error())
return return
} }
e.isModified = false e.IsModified = false
} }
func (g *Gopad) triggerError(errMsg string) { func (g *Gopad) triggerError(errMsg string) {
@ -316,12 +300,12 @@ func (g *Gopad) drawEditors() {
flags = imgui.TabItemFlagsSetSelected flags = imgui.TabItemFlagsSetSelected
} }
if e.isModified { if e.IsModified {
flags |= imgui.TabItemFlagsUnsavedDocument flags |= imgui.TabItemFlagsUnsavedDocument
} }
open := true open := true
if !imgui.BeginTabItemV(e.fileName, &open, flags) { if !imgui.BeginTabItemV(e.FileName, &open, flags) {
if !open { if !open {
g.editorToClose = i g.editorToClose = i
@ -350,19 +334,8 @@ func (g *Gopad) drawEditors() {
imgui.End() imgui.End()
//Draw text area //Draw text area
imgui.SetNextWindowPos(imgui.Vec2{X: g.sidebarWidthPx, Y: g.mainMenuBarHeight + tabsHeight}) g.getActiveEditor().Render(&imgui.Vec2{X: g.sidebarWidthPx, Y: g.mainMenuBarHeight + tabsHeight}, &imgui.Vec2{X: g.winWidth - g.sidebarWidthPx, Y: g.winHeight - g.mainMenuBarHeight - tabsHeight})
fullWinSize := imgui.Vec2{X: g.winWidth - g.sidebarWidthPx, Y: g.winHeight - g.mainMenuBarHeight - tabsHeight}
imgui.SetNextWindowSize(fullWinSize)
imgui.BeginV("editorText", nil, imgui.WindowFlagsNoCollapse|imgui.WindowFlagsNoDecoration|imgui.WindowFlagsNoMove)
imgui.PushStyleColor(imgui.StyleColorFrameBg, g.editorBgColor)
imgui.PushStyleColor(imgui.StyleColorTextSelectedBg, g.textSelectionColor)
fullWinSize.Y -= 18
e := g.getActiveEditor()
imgui.InputTextMultilineV(e.fileName, &e.fileContents, fullWinSize, imgui.ImGuiInputTextFlagsCallbackEdit|imgui.InputTextFlagsAllowTabInput, g.textEditCB)
if shouldForceSwitch || prevActiveEditor != g.activeEditor { if shouldForceSwitch || prevActiveEditor != g.activeEditor {
imgui.SetKeyboardFocusHereV(-1) imgui.SetKeyboardFocusHereV(-1)
} }
@ -372,11 +345,6 @@ func (g *Gopad) drawEditors() {
imgui.End() imgui.End()
} }
func (g *Gopad) textEditCB(d imgui.InputTextCallbackData) int32 {
g.getActiveEditor().isModified = true
return 0
}
func (g *Gopad) getActiveEditor() *Editor { func (g *Gopad) getActiveEditor() *Editor {
return g.getEditor(g.activeEditor) return g.getEditor(g.activeEditor)
} }
@ -384,7 +352,7 @@ func (g *Gopad) getActiveEditor() *Editor {
func (g *Gopad) getEditor(index int) *Editor { func (g *Gopad) getEditor(index int) *Editor {
if len(g.editors) == 0 { if len(g.editors) == 0 {
e := Editor{fileName: "**scratch**"} e := Editor{FileName: "**scratch**"}
g.editors = append(g.editors, e) g.editors = append(g.editors, e)
g.activeEditor = 0 g.activeEditor = 0
return &e return &e
@ -430,7 +398,7 @@ func (g *Gopad) handleFileClick(fPath string) {
for i := 0; i < len(g.editors); i++ { for i := 0; i < len(g.editors); i++ {
e := &g.editors[i] e := &g.editors[i]
if e.filePath == fPath { if e.FilePath == fPath {
editorIndex = i editorIndex = i
break break
} }
@ -443,16 +411,7 @@ func (g *Gopad) handleFileClick(fPath string) {
} }
//Read new file and switch to it //Read new file and switch to it
b, err := os.ReadFile(fPath) g.editors = append(g.editors, *NewEditor(fPath))
if err != nil {
panic(err)
}
g.editors = append(g.editors, Editor{
fileName: filepath.Base(fPath),
filePath: fPath,
fileContents: string(b),
})
g.activeEditor = len(g.editors) - 1 g.activeEditor = len(g.editors) - 1
} }

8
settings/settings.go Executable file
View File

@ -0,0 +1,8 @@
package settings
import "github.com/inkyblackness/imgui-go/v4"
var (
TextSelectionColor imgui.Vec4 = imgui.Vec4{X: 84 / 255.0, Y: 153 / 255.0, Z: 199 / 255.0, W: 0.4}
EditorBgColor imgui.Vec4 = imgui.Vec4{X: 0.1, Y: 0.1, Z: 0.1, W: 1}
)