mirror of
https://github.com/bloeys/gopad.git
synced 2025-12-29 06:58:21 +00:00
Starting custom editor window
This commit is contained in:
189
editor.go
Executable file
189
editor.go
Executable 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
|
||||
}
|
||||
BIN
gopad-icon.ico
BIN
gopad-icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 32 KiB |
BIN
gopad-logo.png
BIN
gopad-logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 34 KiB |
71
main.go
Normal file → Executable file
71
main.go
Normal file → Executable file
@ -16,13 +16,6 @@ import (
|
||||
|
||||
//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 {
|
||||
Win *engine.Window
|
||||
mainFont imgui.Font
|
||||
@ -45,10 +38,6 @@ type Gopad struct {
|
||||
haveErr bool
|
||||
errMsg string
|
||||
|
||||
//Settings
|
||||
textSelectionColor imgui.Vec4
|
||||
editorBgColor imgui.Vec4
|
||||
|
||||
//Cache window size
|
||||
winWidth float32
|
||||
winHeight float32
|
||||
@ -82,14 +71,9 @@ func main() {
|
||||
Win: window,
|
||||
ImGUIInfo: nmageimgui.NewImGUI(),
|
||||
CurrDir: dir,
|
||||
editors: []Editor{
|
||||
{fileName: "**scratch**"},
|
||||
},
|
||||
editors: []Editor{*NewScratchEditor()},
|
||||
editorToClose: -1,
|
||||
|
||||
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)
|
||||
@ -128,9 +112,9 @@ func (g *Gopad) Init() {
|
||||
}
|
||||
|
||||
g.editors = append(g.editors, Editor{
|
||||
fileName: filepath.Base(os.Args[i]),
|
||||
filePath: os.Args[i],
|
||||
fileContents: string(b),
|
||||
FileName: filepath.Base(os.Args[i]),
|
||||
FilePath: os.Args[i],
|
||||
FileContents: string(b),
|
||||
})
|
||||
}
|
||||
|
||||
@ -190,7 +174,7 @@ func (g *Gopad) Update() {
|
||||
|
||||
//Save if needed
|
||||
e := g.getActiveEditor()
|
||||
if !e.isModified {
|
||||
if !e.IsModified {
|
||||
return
|
||||
}
|
||||
|
||||
@ -203,18 +187,18 @@ func (g *Gopad) Update() {
|
||||
|
||||
func (g *Gopad) saveEditor(e *Editor) {
|
||||
|
||||
if e.fileName == "**scratch**" {
|
||||
e.isModified = false
|
||||
if e.FileName == "**scratch**" {
|
||||
e.IsModified = false
|
||||
return
|
||||
}
|
||||
|
||||
err := os.WriteFile(e.filePath, []byte(e.fileContents), os.ModePerm)
|
||||
err := os.WriteFile(e.FilePath, []byte(e.FileContents), os.ModePerm)
|
||||
if err != nil {
|
||||
g.triggerError("Failed to save file. Error: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
e.isModified = false
|
||||
e.IsModified = false
|
||||
}
|
||||
|
||||
func (g *Gopad) triggerError(errMsg string) {
|
||||
@ -316,12 +300,12 @@ func (g *Gopad) drawEditors() {
|
||||
flags = imgui.TabItemFlagsSetSelected
|
||||
}
|
||||
|
||||
if e.isModified {
|
||||
if e.IsModified {
|
||||
flags |= imgui.TabItemFlagsUnsavedDocument
|
||||
}
|
||||
|
||||
open := true
|
||||
if !imgui.BeginTabItemV(e.fileName, &open, flags) {
|
||||
if !imgui.BeginTabItemV(e.FileName, &open, flags) {
|
||||
|
||||
if !open {
|
||||
g.editorToClose = i
|
||||
@ -350,19 +334,8 @@ func (g *Gopad) drawEditors() {
|
||||
imgui.End()
|
||||
|
||||
//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 {
|
||||
imgui.SetKeyboardFocusHereV(-1)
|
||||
}
|
||||
@ -372,11 +345,6 @@ func (g *Gopad) drawEditors() {
|
||||
imgui.End()
|
||||
}
|
||||
|
||||
func (g *Gopad) textEditCB(d imgui.InputTextCallbackData) int32 {
|
||||
g.getActiveEditor().isModified = true
|
||||
return 0
|
||||
}
|
||||
|
||||
func (g *Gopad) getActiveEditor() *Editor {
|
||||
return g.getEditor(g.activeEditor)
|
||||
}
|
||||
@ -384,7 +352,7 @@ func (g *Gopad) getActiveEditor() *Editor {
|
||||
func (g *Gopad) getEditor(index int) *Editor {
|
||||
|
||||
if len(g.editors) == 0 {
|
||||
e := Editor{fileName: "**scratch**"}
|
||||
e := Editor{FileName: "**scratch**"}
|
||||
g.editors = append(g.editors, e)
|
||||
g.activeEditor = 0
|
||||
return &e
|
||||
@ -430,7 +398,7 @@ func (g *Gopad) handleFileClick(fPath string) {
|
||||
for i := 0; i < len(g.editors); i++ {
|
||||
|
||||
e := &g.editors[i]
|
||||
if e.filePath == fPath {
|
||||
if e.FilePath == fPath {
|
||||
editorIndex = i
|
||||
break
|
||||
}
|
||||
@ -443,16 +411,7 @@ func (g *Gopad) handleFileClick(fPath string) {
|
||||
}
|
||||
|
||||
//Read new file and switch to it
|
||||
b, err := os.ReadFile(fPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g.editors = append(g.editors, Editor{
|
||||
fileName: filepath.Base(fPath),
|
||||
filePath: fPath,
|
||||
fileContents: string(b),
|
||||
})
|
||||
g.editors = append(g.editors, *NewEditor(fPath))
|
||||
g.activeEditor = len(g.editors) - 1
|
||||
}
|
||||
|
||||
|
||||
8
settings/settings.go
Executable file
8
settings/settings.go
Executable 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}
|
||||
)
|
||||
Reference in New Issue
Block a user