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
|
package system
|
||||||
|
|
||||||
import "image"
|
import "image"
|
||||||
|
import "unicode"
|
||||||
import "image/color"
|
import "image/color"
|
||||||
import "golang.org/x/image/font"
|
import "golang.org/x/image/font"
|
||||||
import "git.tebibyte.media/tomo/tomo"
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
@ -31,9 +32,11 @@ type textBox struct {
|
|||||||
selecting bool
|
selecting bool
|
||||||
selectStart int
|
selectStart int
|
||||||
dot text.Dot
|
dot text.Dot
|
||||||
|
desiredX fixed.Int26_6
|
||||||
|
|
||||||
drawer typeset.Drawer
|
drawer typeset.Drawer
|
||||||
face util.Cycler[font.Face]
|
face util.Cycler[font.Face]
|
||||||
|
lineHeight util.Memo[fixed.Int26_6]
|
||||||
|
|
||||||
on struct {
|
on struct {
|
||||||
contentBoundsChange event.FuncBroadcaster
|
contentBoundsChange event.FuncBroadcaster
|
||||||
@ -46,6 +49,12 @@ func (this *System) NewTextBox () tomo.TextBox {
|
|||||||
box.box = this.newBox(box)
|
box.box = this.newBox(box)
|
||||||
box.attrTextColor.SetFallback(tomo.ATextColor(color.Black))
|
box.attrTextColor.SetFallback(tomo.ATextColor(color.Black))
|
||||||
box.attrDotColor.SetFallback(tomo.ADotColor(color.RGBA { G: 255, B: 255, A: 255}))
|
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
|
return box
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,13 +98,20 @@ func (this *textBox) SetSelectable (selectable bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *textBox) Select (dot text.Dot) {
|
func (this *textBox) Select (dot text.Dot) {
|
||||||
if !this.selectable { return }
|
if this.selectWithoutResettingDesiredX(dot) {
|
||||||
if this.dot == dot { return }
|
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.SetFocused(true)
|
||||||
this.dot = dot
|
this.dot = dot
|
||||||
this.scrollToDot()
|
this.scrollToDot()
|
||||||
this.on.dotChange.Broadcast()
|
this.on.dotChange.Broadcast()
|
||||||
this.invalidateDraw()
|
this.invalidateDraw()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *textBox) Dot () text.Dot {
|
func (this *textBox) Dot () text.Dot {
|
||||||
@ -351,6 +367,42 @@ func (this *textBox) handleKeyDown (key input.Key, numberPad bool) bool {
|
|||||||
dot := this.Dot()
|
dot := this.Dot()
|
||||||
sel := modifiers.Shift
|
sel := modifiers.Shift
|
||||||
word := modifiers.Control
|
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 {
|
switch {
|
||||||
case key == input.KeyHome || (modifiers.Alt && key == input.KeyLeft):
|
case key == input.KeyHome || (modifiers.Alt && key == input.KeyLeft):
|
||||||
dot.End = 0
|
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))
|
this.Select(text.MoveRight(this.runes, dot, word))
|
||||||
}
|
}
|
||||||
return true
|
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:
|
case key == input.Key('a') && modifiers.Control:
|
||||||
dot.Start = 0
|
dot.Start = 0
|
||||||
dot.End = len(this.text)
|
dot.End = len(this.text)
|
||||||
@ -396,6 +454,10 @@ func (this *textBox) handleKeyUp (key input.Key, numberPad bool) bool {
|
|||||||
return true
|
return true
|
||||||
case key == input.KeyEnd || (modifiers.Alt && key == input.KeyRight):
|
case key == input.KeyEnd || (modifiers.Alt && key == input.KeyRight):
|
||||||
return true
|
return true
|
||||||
|
case key == input.KeyUp:
|
||||||
|
return true
|
||||||
|
case key == input.KeyDown:
|
||||||
|
return true
|
||||||
case key == input.KeyLeft:
|
case key == input.KeyLeft:
|
||||||
return true
|
return true
|
||||||
case key == input.KeyRight:
|
case key == input.KeyRight:
|
||||||
@ -502,6 +564,7 @@ func (this *textBox) handleFaceChange () {
|
|||||||
this.drawer.SetFace(face)
|
this.drawer.SetFace(face)
|
||||||
this.invalidateMinimum()
|
this.invalidateMinimum()
|
||||||
this.invalidateLayout()
|
this.invalidateLayout()
|
||||||
|
this.lineHeight.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *textBox) recursiveReApply () {
|
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