Correct cursor positioning+proper scrolling

This commit is contained in:
bloeys
2022-02-27 14:06:39 +04:00
parent f562ec674b
commit eb3ce8e466
5 changed files with 67 additions and 35 deletions

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
} }

View File

@ -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
) )