mirror of
https://github.com/bloeys/gopad.git
synced 2025-12-29 15:08:21 +00:00
Correct cursor positioning+proper scrolling
This commit is contained in:
69
editor.go
69
editor.go
@ -39,6 +39,9 @@ type Editor struct {
|
|||||||
LinesHead *LinesNode
|
LinesHead *LinesNode
|
||||||
LineCount int
|
LineCount int
|
||||||
|
|
||||||
|
LineHeight float32
|
||||||
|
CharWidth float32
|
||||||
|
|
||||||
StartPos float32
|
StartPos float32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +54,23 @@ func (e *Editor) SetStartPos(mouseDeltaNorm int32) {
|
|||||||
e.StartPos = clampF32(e.StartPos+float32(-mouseDeltaNorm)*settings.ScrollSpeed, 0, float32(e.LineCount))
|
e.StartPos = clampF32(e.StartPos+float32(-mouseDeltaNorm)*settings.ScrollSpeed, 0, float32(e.LineCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Editor) RefreshFontSettings() {
|
||||||
|
e.LineHeight = imgui.TextLineHeightWithSpacing()
|
||||||
|
|
||||||
|
//NOTE: Because of 'https://github.com/ocornut/imgui/issues/792', CalcTextSize returns slightly incorrect width
|
||||||
|
//values for sentences than to be expected with singleCharWidth*sentenceCharCount. For example, with 3 chars at width 10
|
||||||
|
//we expect width of 30 (for a fixed-width font), but instead we might get 29.
|
||||||
|
// This is fixed in the newer releases, but imgui-go hasn't updated yet.
|
||||||
|
//
|
||||||
|
//That's why instead of getting width of one char, we get the average width from the width of a sentence, which helps us position
|
||||||
|
//cursors properly for now
|
||||||
|
e.CharWidth = imgui.CalcTextSize("abcdefghijklmnopqrstuvwxyz", false, 1000).X / 26
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Editor) RoundToNearestChar(x float32) float32 {
|
||||||
|
return float32(math.Round(float64(x/e.CharWidth))) * e.CharWidth
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Editor) Render(drawStartPos, winSize *imgui.Vec2) {
|
func (e *Editor) Render(drawStartPos, winSize *imgui.Vec2) {
|
||||||
|
|
||||||
//Draw window
|
//Draw window
|
||||||
@ -67,52 +87,42 @@ func (e *Editor) Render(drawStartPos, winSize *imgui.Vec2) {
|
|||||||
paddedDrawStartPos := *drawStartPos
|
paddedDrawStartPos := *drawStartPos
|
||||||
|
|
||||||
//Draw lines
|
//Draw lines
|
||||||
lineHeight := imgui.TextLineHeightWithSpacing()
|
linesToDraw := int(winSize.Y / e.LineHeight)
|
||||||
charWidth := imgui.CalcTextSize("a", false, 1000).X
|
|
||||||
linesToDraw := int(winSize.Y / lineHeight)
|
|
||||||
// println("Lines to draw:", linesToDraw)
|
// println("Lines to draw:", linesToDraw)
|
||||||
|
|
||||||
dl := imgui.WindowDrawList()
|
dl := imgui.WindowDrawList()
|
||||||
startLine := clampInt(int(e.StartPos), 0, e.LineCount)
|
startLine := clampInt(int(e.StartPos), 0, e.LineCount)
|
||||||
println("Start Pos: ", e.StartPos, "; Start line:", startLine)
|
|
||||||
for i := startLine; i < startLine+linesToDraw; i++ {
|
for i := startLine; i < startLine+linesToDraw; i++ {
|
||||||
dl.AddText(*drawStartPos, imgui.PackedColorFromVec4(imgui.Vec4{X: 1, Y: 1, Z: 1, W: 1}), string(e.GetLine(0+i).chars))
|
dl.AddText(*drawStartPos, imgui.PackedColorFromVec4(imgui.Vec4{X: 1, Y: 1, Z: 1, W: 1}), string(e.GetLine(0+i).chars))
|
||||||
drawStartPos.Y += lineHeight
|
drawStartPos.Y += e.LineHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
//Draw cursor
|
//Calculate position of cursor in window and grid coords.
|
||||||
cx := clampInt(e.MouseX-int(paddedDrawStartPos.X), 0, int(winSize.X))
|
//Window coords are as reported by SDL, but we correct for padding and snap to the nearest
|
||||||
cy := clampInt(e.MouseY-int(paddedDrawStartPos.Y), 0, int(winSize.Y))
|
//char window pos.
|
||||||
|
//
|
||||||
|
//Since gopad only supports fixed-width fonts, we treat the text area as a grid with each
|
||||||
|
//cell having identical width and one char.
|
||||||
|
clickedColWindowY := clampInt(e.MouseY-int(paddedDrawStartPos.Y), 0, math.MaxInt)
|
||||||
|
clickedColGridY := clampInt(clickedColWindowY/int(e.LineHeight), 0, e.LineCount)
|
||||||
|
|
||||||
clickedLine := clampInt(cy/int(lineHeight), 0, e.LineCount)
|
clickedColWindowX := clampInt(int(e.RoundToNearestChar(float32(e.MouseX))), 0, math.MaxInt)
|
||||||
clickedCol := cx / int(charWidth)
|
clickedColGridX := clickedColWindowX / int(e.CharWidth)
|
||||||
// fmt.Printf("line,col: %v,%v\n", clickedLine, clickedCol)
|
|
||||||
|
|
||||||
eee := e.GetLine(clickedLine)
|
clickedLine := e.GetLine(startLine + clickedColGridY)
|
||||||
tabCount, tabChars := getTabs(eee, clickedCol)
|
tabCount, _ := getTabs(clickedLine, clickedColGridX)
|
||||||
|
|
||||||
maxCol := len(eee.chars) - 1
|
textWidth := float32(len(clickedLine.chars)-tabCount+tabCount*settings.TabSize) * e.CharWidth
|
||||||
if tabCount > 0 {
|
lineX := clampF32(float32(clickedColWindowX), 0, paddedDrawStartPos.X+textWidth)
|
||||||
maxCol += clampInt(tabCount*settings.TabSize, 0, math.MaxInt)
|
|
||||||
}
|
|
||||||
finalCol := clampInt(clickedCol+tabChars, 0, maxCol)
|
|
||||||
// if len(eee.chars) > 0 && finalCol > 0 {
|
|
||||||
// x := finalCol - tabCount*settings.TabSize
|
|
||||||
// println("!!!!", len(string(eee.chars)), "; C:", string(eee.chars[x]))
|
|
||||||
// }
|
|
||||||
|
|
||||||
lineX := paddedDrawStartPos.X + float32(finalCol)*charWidth
|
|
||||||
lineStart := imgui.Vec2{
|
lineStart := imgui.Vec2{
|
||||||
X: lineX,
|
X: lineX,
|
||||||
Y: paddedDrawStartPos.Y + float32(clickedLine)*lineHeight - lineHeight*0.25,
|
Y: paddedDrawStartPos.Y + float32(clickedColGridY)*e.LineHeight - e.LineHeight*0.25,
|
||||||
}
|
}
|
||||||
lineEnd := imgui.Vec2{
|
lineEnd := imgui.Vec2{
|
||||||
X: lineX,
|
X: lineX,
|
||||||
Y: paddedDrawStartPos.Y + float32(clickedLine)*lineHeight + lineHeight*0.75,
|
Y: paddedDrawStartPos.Y + float32(clickedColGridY)*e.LineHeight + e.LineHeight*0.75,
|
||||||
}
|
}
|
||||||
|
dl.AddLineV(lineStart, lineEnd, imgui.PackedColorFromVec4(imgui.Vec4{Z: 0.7, W: 1}), settings.CursorWidthFactor*e.CharWidth)
|
||||||
thickness := 0.2 * charWidth
|
|
||||||
dl.AddLineV(lineStart, lineEnd, imgui.PackedColorFromVec4(imgui.Vec4{Z: 0.7, W: 1}), thickness)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTabs(l *Line, col int) (tabCount, charsToOffsetBy int) {
|
func getTabs(l *Line, col int) (tabCount, charsToOffsetBy int) {
|
||||||
@ -247,6 +257,7 @@ func NewScratchEditor() *Editor {
|
|||||||
FileName: "**scratch**",
|
FileName: "**scratch**",
|
||||||
LinesHead: NewLineNode(),
|
LinesHead: NewLineNode(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
go.mod
3
go.mod
@ -3,12 +3,13 @@ module github.com/bloeys/gopad
|
|||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bloeys/nmage v0.0.8
|
github.com/bloeys/nmage v0.0.10
|
||||||
github.com/inkyblackness/imgui-go/v4 v4.3.0
|
github.com/inkyblackness/imgui-go/v4 v4.3.0
|
||||||
github.com/veandco/go-sdl2 v0.4.14
|
github.com/veandco/go-sdl2 v0.4.14
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/bloeys/assimp-go v0.4.2 // indirect
|
||||||
github.com/bloeys/gglm v0.3.1 // indirect
|
github.com/bloeys/gglm v0.3.1 // indirect
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
5
go.sum
5
go.sum
@ -1,8 +1,13 @@
|
|||||||
|
github.com/bloeys/assimp-go v0.4.2 h1:ArVK74BCFcTO/rCGj2NgZG9xtbjnJdEn5npIeJx1Z04=
|
||||||
github.com/bloeys/assimp-go v0.4.2/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0=
|
github.com/bloeys/assimp-go v0.4.2/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0=
|
||||||
github.com/bloeys/gglm v0.3.1 h1:Sy9upW7SBsBfDXrSmEhid3aQ+7J7itej+upwcxOnPMQ=
|
github.com/bloeys/gglm v0.3.1 h1:Sy9upW7SBsBfDXrSmEhid3aQ+7J7itej+upwcxOnPMQ=
|
||||||
github.com/bloeys/gglm v0.3.1/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
|
github.com/bloeys/gglm v0.3.1/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
|
||||||
github.com/bloeys/nmage v0.0.8 h1:HCoEaTBWTucnXrjQ+8OCUTzG/3rjpV1eliXWUW44+FY=
|
github.com/bloeys/nmage v0.0.8 h1:HCoEaTBWTucnXrjQ+8OCUTzG/3rjpV1eliXWUW44+FY=
|
||||||
github.com/bloeys/nmage v0.0.8/go.mod h1:4h2tKtMvk9ab8r/+rem4QonPXEBTho6VWvpCMm0M6iM=
|
github.com/bloeys/nmage v0.0.8/go.mod h1:4h2tKtMvk9ab8r/+rem4QonPXEBTho6VWvpCMm0M6iM=
|
||||||
|
github.com/bloeys/nmage v0.0.9 h1:dP0g8e7VeygbX5AcYLTIPheLJr+CZmG2uOICQfcDTDA=
|
||||||
|
github.com/bloeys/nmage v0.0.9/go.mod h1:4h2tKtMvk9ab8r/+rem4QonPXEBTho6VWvpCMm0M6iM=
|
||||||
|
github.com/bloeys/nmage v0.0.10 h1:6Ryl3qjEbXxeIL+9BnBxOrGaW9mKObyY/JbRYkR8ogQ=
|
||||||
|
github.com/bloeys/nmage v0.0.10/go.mod h1:4h2tKtMvk9ab8r/+rem4QonPXEBTho6VWvpCMm0M6iM=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||||
|
|||||||
23
main.go
23
main.go
@ -6,9 +6,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/bloeys/gopad/settings"
|
||||||
"github.com/bloeys/nmage/engine"
|
"github.com/bloeys/nmage/engine"
|
||||||
"github.com/bloeys/nmage/input"
|
"github.com/bloeys/nmage/input"
|
||||||
"github.com/bloeys/nmage/logging"
|
"github.com/bloeys/nmage/logging"
|
||||||
|
"github.com/bloeys/nmage/renderer/rend3dgl"
|
||||||
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
||||||
"github.com/inkyblackness/imgui-go/v4"
|
"github.com/inkyblackness/imgui-go/v4"
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
@ -56,7 +58,7 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
window, err := engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE|engine.WindowFlags_ALLOW_HIGHDPI)
|
window, err := engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE|engine.WindowFlags_ALLOW_HIGHDPI, rend3dgl.NewRend3DGL())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.ErrLog.Fatalln("Failed to create window. Err: ", err)
|
logging.ErrLog.Fatalln("Failed to create window. Err: ", err)
|
||||||
}
|
}
|
||||||
@ -86,13 +88,12 @@ func (g *Gopad) Init() {
|
|||||||
g.Win.EventCallbacks = append(g.Win.EventCallbacks, g.handleWindowEvents)
|
g.Win.EventCallbacks = append(g.Win.EventCallbacks, g.handleWindowEvents)
|
||||||
|
|
||||||
//Setup font
|
//Setup font
|
||||||
var fontSize float32 = 16
|
|
||||||
fConfig := imgui.NewFontConfig()
|
fConfig := imgui.NewFontConfig()
|
||||||
defer fConfig.Delete()
|
defer fConfig.Delete()
|
||||||
|
|
||||||
fConfig.SetOversampleH(2)
|
fConfig.SetOversampleH(2)
|
||||||
fConfig.SetOversampleV(2)
|
fConfig.SetOversampleV(2)
|
||||||
g.mainFont = g.ImGUIInfo.AddFontTTF("./res/fonts/courier-prime.regular.ttf", fontSize, &fConfig, nil)
|
g.mainFont = g.ImGUIInfo.AddFontTTF("./res/fonts/courier-prime.regular.ttf", settings.FontSize, &fConfig, nil)
|
||||||
|
|
||||||
//Sidebar
|
//Sidebar
|
||||||
g.CurrDirContents = getDirContents(g.CurrDir)
|
g.CurrDirContents = getDirContents(g.CurrDir)
|
||||||
@ -121,6 +122,15 @@ func (g *Gopad) Init() {
|
|||||||
g.activeEditor = len(g.editors) - 1
|
g.activeEditor = len(g.editors) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Gopad) Start() {
|
||||||
|
imgui.PushFont(g.mainFont)
|
||||||
|
for i := 0; i < len(g.editors); i++ {
|
||||||
|
e := &g.editors[i]
|
||||||
|
e.RefreshFontSettings()
|
||||||
|
}
|
||||||
|
imgui.PopFont()
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Gopad) handleWindowEvents(event sdl.Event) {
|
func (g *Gopad) handleWindowEvents(event sdl.Event) {
|
||||||
|
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
@ -361,7 +371,8 @@ 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 := *NewScratchEditor()
|
||||||
|
e.RefreshFontSettings()
|
||||||
g.editors = append(g.editors, e)
|
g.editors = append(g.editors, e)
|
||||||
g.activeEditor = 0
|
g.activeEditor = 0
|
||||||
return &e
|
return &e
|
||||||
@ -420,7 +431,9 @@ func (g *Gopad) handleFileClick(fPath string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Read new file and switch to it
|
//Read new file and switch to it
|
||||||
g.editors = append(g.editors, *NewEditor(fPath))
|
e := *NewEditor(fPath)
|
||||||
|
e.RefreshFontSettings()
|
||||||
|
g.editors = append(g.editors, e)
|
||||||
g.activeEditor = len(g.editors) - 1
|
g.activeEditor = len(g.editors) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,10 @@ package settings
|
|||||||
import "github.com/inkyblackness/imgui-go/v4"
|
import "github.com/inkyblackness/imgui-go/v4"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
FontSize float32 = 16
|
||||||
TextSelectionColor imgui.Vec4 = imgui.Vec4{X: 84 / 255.0, Y: 153 / 255.0, Z: 199 / 255.0, W: 0.4}
|
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}
|
EditorBgColor imgui.Vec4 = imgui.Vec4{X: 0.1, Y: 0.1, Z: 0.1, W: 1}
|
||||||
TabSize int = 4
|
TabSize int = 4
|
||||||
ScrollSpeed float32 = 4
|
ScrollSpeed float32 = 4
|
||||||
|
CursorWidthFactor float32 = 0.15
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user