Rudimentary text selection with the mouse
This commit is contained in:
parent
88502cf628
commit
d18da8b07a
@ -10,6 +10,7 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||
|
||||
type characterLayout struct {
|
||||
x int
|
||||
width int
|
||||
character rune
|
||||
}
|
||||
|
||||
@ -38,13 +39,14 @@ const (
|
||||
// text, and calculating text bounds. It avoids doing redundant work
|
||||
// automatically.
|
||||
type TextDrawer struct {
|
||||
runes []rune
|
||||
face font.Face
|
||||
width int
|
||||
height int
|
||||
align Align
|
||||
wrap bool
|
||||
cut bool
|
||||
runes []rune
|
||||
face font.Face
|
||||
width int
|
||||
height int
|
||||
align Align
|
||||
wrap bool
|
||||
cut bool
|
||||
metrics font.Metrics
|
||||
|
||||
layout []wordLayout
|
||||
layoutClean bool
|
||||
@ -205,6 +207,38 @@ func (drawer *TextDrawer) PositionOf (index int) (position image.Point) {
|
||||
return
|
||||
}
|
||||
|
||||
// AtPosition returns the index at the specified position relative to the
|
||||
// baseline.
|
||||
func (drawer *TextDrawer) AtPosition (position image.Point) (index int) {
|
||||
cursor := 0
|
||||
if !drawer.layoutClean { drawer.recalculate() }
|
||||
for _, word := range drawer.layout {
|
||||
for _, character := range word.text {
|
||||
bounds := drawer.boundsOfChar(character).Add(word.position)
|
||||
if position.In(bounds) {
|
||||
return cursor
|
||||
}
|
||||
cursor ++
|
||||
}
|
||||
for _, character := range word.whitespace {
|
||||
bounds := drawer.boundsOfChar(character).Add(word.position)
|
||||
if position.In(bounds) {
|
||||
return cursor
|
||||
}
|
||||
cursor ++
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (drawer *TextDrawer) boundsOfChar (char characterLayout) (image.Rectangle) {
|
||||
return image.Rect (
|
||||
char.x, 0,
|
||||
char.x + char.width,
|
||||
drawer.metrics.Height.Ceil()).
|
||||
Sub(image.Pt(0, drawer.metrics.Descent.Round()))
|
||||
}
|
||||
|
||||
// Length returns the amount of runes in the drawer's text.
|
||||
func (drawer *TextDrawer) Length () (length int) {
|
||||
return len(drawer.runes)
|
||||
@ -217,7 +251,7 @@ func (drawer *TextDrawer) recalculate () {
|
||||
if drawer.runes == nil { return }
|
||||
if drawer.face == nil { return }
|
||||
|
||||
metrics := drawer.face.Metrics()
|
||||
drawer.metrics = drawer.face.Metrics()
|
||||
dot := fixed.Point26_6 { 0, 0 }
|
||||
index := 0
|
||||
horizontalExtent := 0
|
||||
@ -241,6 +275,7 @@ func (drawer *TextDrawer) recalculate () {
|
||||
word.text = append(word.text, characterLayout {
|
||||
x: currentCharacterX.Round(),
|
||||
character: character,
|
||||
width: advance.Ceil(),
|
||||
})
|
||||
|
||||
dot.X += advance
|
||||
@ -264,9 +299,9 @@ func (drawer *TextDrawer) recalculate () {
|
||||
word.width + word.position.X > drawer.width &&
|
||||
word.position.X > 0 {
|
||||
|
||||
word.position.Y += metrics.Height.Round()
|
||||
word.position.Y += drawer.metrics.Height.Round()
|
||||
word.position.X = 0
|
||||
dot.Y += metrics.Height
|
||||
dot.Y += drawer.metrics.Height
|
||||
dot.X = wordWidth
|
||||
}
|
||||
|
||||
@ -281,12 +316,13 @@ func (drawer *TextDrawer) recalculate () {
|
||||
word.whitespace = append(word.whitespace, characterLayout {
|
||||
x: currentCharacterX.Round(),
|
||||
character: character,
|
||||
width: advance.Ceil(),
|
||||
})
|
||||
spaceWidth += advance
|
||||
currentCharacterX += advance
|
||||
|
||||
if character == '\n' {
|
||||
dot.Y += metrics.Height
|
||||
dot.Y += drawer.metrics.Height
|
||||
dot.X = 0
|
||||
word.breaksAfter ++
|
||||
break
|
||||
@ -309,8 +345,9 @@ func (drawer *TextDrawer) recalculate () {
|
||||
// stop processing more words. and remove any words that have
|
||||
// also crossed the line.
|
||||
if
|
||||
drawer.cut &&
|
||||
(dot.Y - metrics.Ascent - metrics.Descent).Round() >
|
||||
drawer.cut && (
|
||||
dot.Y - drawer.metrics.Ascent -
|
||||
drawer.metrics.Descent).Round() >
|
||||
drawer.height {
|
||||
|
||||
for
|
||||
@ -343,11 +380,15 @@ func (drawer *TextDrawer) recalculate () {
|
||||
}
|
||||
|
||||
if drawer.cut {
|
||||
drawer.layoutBounds.Min.Y = 0 - metrics.Ascent.Round()
|
||||
drawer.layoutBounds.Max.Y = drawer.height - metrics.Ascent.Round()
|
||||
drawer.layoutBounds.Min.Y = 0 - drawer.metrics.Ascent.Round()
|
||||
drawer.layoutBounds.Max.Y =
|
||||
drawer.height -
|
||||
drawer.metrics.Ascent.Round()
|
||||
} else {
|
||||
drawer.layoutBounds.Min.Y = 0 - metrics.Ascent.Round()
|
||||
drawer.layoutBounds.Max.Y = dot.Y.Round() + metrics.Descent.Round()
|
||||
drawer.layoutBounds.Min.Y = 0 - drawer.metrics.Ascent.Round()
|
||||
drawer.layoutBounds.Max.Y =
|
||||
dot.Y.Round() +
|
||||
drawer.metrics.Descent.Round()
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
@ -14,7 +14,8 @@ type TextBox struct {
|
||||
*core.FocusableCore
|
||||
core core.CoreControl
|
||||
focusableControl core.FocusableCoreControl
|
||||
|
||||
|
||||
dragging bool
|
||||
dot textmanip.Dot
|
||||
scroll int
|
||||
placeholder string
|
||||
@ -63,10 +64,44 @@ func (element *TextBox) handleResize () {
|
||||
func (element *TextBox) HandleMouseDown (x, y int, button input.Button) {
|
||||
if !element.Enabled() { return }
|
||||
if !element.Focused() { element.Focus() }
|
||||
|
||||
if button == input.ButtonLeft {
|
||||
point := image.Pt(x, y)
|
||||
offset := element.Bounds().Min.Add (image.Pt (
|
||||
element.config.Padding() - element.scroll,
|
||||
element.config.Padding()))
|
||||
runeIndex := element.valueDrawer.AtPosition(point.Sub(offset))
|
||||
element.dragging = true
|
||||
if runeIndex > -1 {
|
||||
element.dot = textmanip.EmptyDot(runeIndex)
|
||||
element.redo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseMove (x, y int) {
|
||||
if !element.Enabled() { return }
|
||||
if !element.Focused() { element.Focus() }
|
||||
|
||||
if element.dragging {
|
||||
point := image.Pt(x, y)
|
||||
offset := element.Bounds().Min.Add (image.Pt (
|
||||
element.config.Padding() - element.scroll,
|
||||
element.config.Padding()))
|
||||
runeIndex := element.valueDrawer.AtPosition(point.Sub(offset))
|
||||
if runeIndex > -1 {
|
||||
element.dot.End = runeIndex
|
||||
element.redo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseUp (x, y int, button input.Button) {
|
||||
if button == input.ButtonLeft {
|
||||
element.dragging = false
|
||||
}
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseUp (x, y int, button input.Button) { }
|
||||
func (element *TextBox) HandleMouseMove (x, y int) { }
|
||||
func (element *TextBox) HandleMouseScroll (x, y int, deltaX, deltaY float64) { }
|
||||
|
||||
func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) {
|
||||
|
Reference in New Issue
Block a user