Add up/down keynav
Paragraph jumping could be better, but that can be refined later. Progress on #10
This commit is contained in:
parent
c1c0d2125d
commit
2f828b1ae8
@ -1,6 +1,7 @@
|
||||
package system
|
||||
|
||||
import "image"
|
||||
import "unicode"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
@ -31,9 +32,11 @@ type textBox struct {
|
||||
selecting bool
|
||||
selectStart int
|
||||
dot text.Dot
|
||||
desiredX fixed.Int26_6
|
||||
|
||||
drawer typeset.Drawer
|
||||
face util.Cycler[font.Face]
|
||||
drawer typeset.Drawer
|
||||
face util.Cycler[font.Face]
|
||||
lineHeight util.Memo[fixed.Int26_6]
|
||||
|
||||
on struct {
|
||||
contentBoundsChange event.FuncBroadcaster
|
||||
@ -46,6 +49,12 @@ func (this *System) NewTextBox () tomo.TextBox {
|
||||
box.box = this.newBox(box)
|
||||
box.attrTextColor.SetFallback(tomo.ATextColor(color.Black))
|
||||
box.attrDotColor.SetFallback(tomo.ADotColor(color.RGBA { G: 255, B: 255, A: 255}))
|
||||
box.lineHeight = util.NewMemo(func () fixed.Int26_6 {
|
||||
face := box.face.Value()
|
||||
if face == nil { return 0 }
|
||||
metrics := face.Metrics()
|
||||
return metrics.Height
|
||||
})
|
||||
return box
|
||||
}
|
||||
|
||||
@ -89,13 +98,20 @@ func (this *textBox) SetSelectable (selectable bool) {
|
||||
}
|
||||
|
||||
func (this *textBox) Select (dot text.Dot) {
|
||||
if !this.selectable { return }
|
||||
if this.dot == dot { return }
|
||||
if this.selectWithoutResettingDesiredX(dot) {
|
||||
this.desiredX = fixed.I(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *textBox) selectWithoutResettingDesiredX (dot text.Dot) bool {
|
||||
if !this.selectable { return false }
|
||||
if this.dot == dot { return false }
|
||||
this.SetFocused(true)
|
||||
this.dot = dot
|
||||
this.scrollToDot()
|
||||
this.on.dotChange.Broadcast()
|
||||
this.invalidateDraw()
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *textBox) Dot () text.Dot {
|
||||
@ -351,6 +367,42 @@ func (this *textBox) handleKeyDown (key input.Key, numberPad bool) bool {
|
||||
dot := this.Dot()
|
||||
sel := modifiers.Shift
|
||||
word := modifiers.Control
|
||||
|
||||
moveVertically := func (delta fixed.Int26_6) {
|
||||
currentDot := 0
|
||||
if sel {
|
||||
currentDot = dot.End
|
||||
} else {
|
||||
currentDot = dot.Canon().Start
|
||||
if delta > fixed.I(0) { currentDot = dot.Canon().End }
|
||||
}
|
||||
|
||||
nextDot := 0
|
||||
if word {
|
||||
if delta > fixed.I(0) {
|
||||
nextDot = nextParagraph(this.runes, currentDot)
|
||||
} else {
|
||||
nextDot = previousParagraph(this.runes, currentDot)
|
||||
}
|
||||
} else {
|
||||
currentPosition := this.drawer.PositionAt(currentDot)
|
||||
if this.desiredX != fixed.I(0) {
|
||||
currentPosition.X = this.desiredX
|
||||
}
|
||||
nextPosition := currentPosition
|
||||
nextPosition.Y += this.lineHeight.Value().Mul(delta)
|
||||
this.desiredX = nextPosition.X
|
||||
nextDot = this.drawer.AtPosition(nextPosition)
|
||||
}
|
||||
|
||||
if sel {
|
||||
dot.End = nextDot
|
||||
this.selectWithoutResettingDesiredX(dot)
|
||||
} else {
|
||||
this.selectWithoutResettingDesiredX(text.EmptyDot(nextDot))
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case key == input.KeyHome || (modifiers.Alt && key == input.KeyLeft):
|
||||
dot.End = 0
|
||||
@ -376,6 +428,12 @@ func (this *textBox) handleKeyDown (key input.Key, numberPad bool) bool {
|
||||
this.Select(text.MoveRight(this.runes, dot, word))
|
||||
}
|
||||
return true
|
||||
case key == input.KeyUp:
|
||||
moveVertically(fixed.I(-1))
|
||||
return true
|
||||
case key == input.KeyDown:
|
||||
moveVertically(fixed.I(1))
|
||||
return true
|
||||
case key == input.Key('a') && modifiers.Control:
|
||||
dot.Start = 0
|
||||
dot.End = len(this.text)
|
||||
@ -396,6 +454,10 @@ func (this *textBox) handleKeyUp (key input.Key, numberPad bool) bool {
|
||||
return true
|
||||
case key == input.KeyEnd || (modifiers.Alt && key == input.KeyRight):
|
||||
return true
|
||||
case key == input.KeyUp:
|
||||
return true
|
||||
case key == input.KeyDown:
|
||||
return true
|
||||
case key == input.KeyLeft:
|
||||
return true
|
||||
case key == input.KeyRight:
|
||||
@ -502,6 +564,7 @@ func (this *textBox) handleFaceChange () {
|
||||
this.drawer.SetFace(face)
|
||||
this.invalidateMinimum()
|
||||
this.invalidateLayout()
|
||||
this.lineHeight.Invalidate()
|
||||
}
|
||||
|
||||
func (this *textBox) recursiveReApply () {
|
||||
@ -523,3 +586,32 @@ func (this *textBox) recursiveReApply () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func previousParagraph (text []rune, index int) int {
|
||||
consecLF := 0
|
||||
if index >= len(text) { index = len(text) - 1 }
|
||||
for ; index > 0; index -- {
|
||||
char := text[index]
|
||||
if char == '\n' {
|
||||
consecLF ++
|
||||
} else if !unicode.IsSpace(char) {
|
||||
if consecLF >= 2 { return index + 1 }
|
||||
consecLF = 0
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
func nextParagraph (text []rune, index int) int {
|
||||
consecLF := 0
|
||||
for ; index < len(text); index ++ {
|
||||
char := text[index]
|
||||
if char == '\n' {
|
||||
consecLF ++
|
||||
} else if !unicode.IsSpace(char) {
|
||||
if consecLF >= 2 { return index }
|
||||
consecLF = 0
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user