This repository has been archived on 2024-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
x/textbox.go

167 lines
3.8 KiB
Go
Raw Normal View History

2023-07-02 06:52:14 +00:00
package x
2023-07-12 05:17:12 +00:00
import "image"
import "image/color"
import "golang.org/x/image/font"
2023-07-02 06:52:14 +00:00
import "git.tebibyte.media/tomo/tomo"
2023-07-12 05:17:12 +00:00
import "git.tebibyte.media/tomo/typeset"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/canvas"
type textBox struct {
*box
hOverflow, vOverflow bool
contentBounds image.Rectangle
scroll image.Point
text string
textColor color.Color
face font.Face
2023-07-13 16:48:09 +00:00
wrap bool
2023-07-12 05:17:12 +00:00
hAlign tomo.Align
drawer typeset.Drawer
on struct {
contentBoundsChange event.FuncBroadcaster
}
}
2023-07-02 06:52:14 +00:00
func (backend *Backend) NewTextBox() tomo.TextBox {
2023-07-12 05:17:12 +00:00
box := &textBox {
box: backend.NewBox().(*box),
textColor: color.Black,
}
box.box.drawer = box
box.outer = box
return box
}
func (this *textBox) SetOverflow (horizontal, vertical bool) {
if this.hOverflow == horizontal && this.vOverflow == vertical { return }
this.hOverflow = horizontal
this.vOverflow = vertical
this.invalidateLayout()
}
func (this *textBox) ContentBounds () image.Rectangle {
return this.contentBounds
}
func (this *textBox) ScrollTo (point image.Point) {
// TODO: constrain scroll
this.scroll = point
this.invalidateLayout()
}
func (this *textBox) OnContentBoundsChange (callback func()) event.Cookie {
return this.on.contentBoundsChange.Connect(callback)
}
func (this *textBox) SetText (text string) {
if this.text == text { return }
this.text = text
this.drawer.SetText([]rune(text))
this.recalculateMinimumSize()
this.invalidateLayout()
}
func (this *textBox) SetTextColor (c color.Color) {
if this.textColor == c { return }
this.textColor = c
this.invalidateDraw()
}
func (this *textBox) SetFace (face font.Face) {
if this.face == face { return }
this.face = face
this.drawer.SetFace(face)
this.recalculateMinimumSize()
this.invalidateLayout()
}
2023-07-13 16:48:09 +00:00
func (this *textBox) SetWrap (wrap bool) {
if this.wrap == wrap { return }
this.wrap = wrap
this.recalculateMinimumSize()
this.invalidateLayout()
}
2023-07-12 05:17:12 +00:00
2023-07-13 16:48:09 +00:00
func (this *textBox) SetAlign (x, y tomo.Align) {
if this.hAlign == x { return }
this.hAlign = x
switch x {
2023-07-12 05:17:12 +00:00
case tomo.AlignStart: this.drawer.SetAlign(typeset.AlignLeft)
case tomo.AlignMiddle: this.drawer.SetAlign(typeset.AlignCenter)
case tomo.AlignEnd: this.drawer.SetAlign(typeset.AlignRight)
case tomo.AlignEven: this.drawer.SetAlign(typeset.AlignJustify)
}
this.invalidateDraw()
}
func (this *textBox) Draw (can canvas.Canvas) {
if can == nil { return }
this.drawBorders(can)
pen := can.Pen()
pen.Fill(this.color)
pen.Rectangle(can.Bounds())
if this.face == nil { return }
offset := this.InnerBounds().Min.
Sub(this.scroll).
Sub(this.drawer.LayoutBoundsSpace().Min)
this.drawer.Draw(can, this.textColor, offset)
}
2023-07-13 16:48:09 +00:00
func (this *textBox) normalizedLayoutBoundsSpace () image.Rectangle {
bounds := this.drawer.LayoutBoundsSpace()
return bounds.Sub(bounds.Min)
}
2023-07-12 05:17:12 +00:00
func (this *textBox) recalculateMinimumSize () {
2023-07-13 16:48:09 +00:00
minimum := image.Pt (
this.drawer.Em().Round(),
this.drawer.LineHeight().Round())
textSize := this.normalizedLayoutBoundsSpace()
if !this.hOverflow && !this.wrap {
minimum.X = textSize.Dx()
}
if !this.vOverflow {
minimum.Y = textSize.Dy()
}
minimum.X += this.padding.Horizontal()
minimum.Y += this.padding.Vertical()
borderSum := this.borderSum()
minimum.X += borderSum.Horizontal()
minimum.Y += borderSum.Vertical()
this.SetMinimumSize(minimum)
2023-07-12 05:17:12 +00:00
}
func (this *textBox) doLayout () {
this.box.doLayout()
previousContentBounds := this.contentBounds
innerBounds := this.InnerBounds()
2023-07-13 16:48:09 +00:00
if this.hOverflow && !this.wrap {
this.drawer.SetMaxWidth(0)
} else {
this.drawer.SetMaxWidth(innerBounds.Dx())
}
if this.vOverflow {
this.drawer.SetMaxHeight(0)
} else {
this.drawer.SetMaxHeight(innerBounds.Dy())
}
2023-07-12 05:17:12 +00:00
2023-07-13 16:48:09 +00:00
this.contentBounds = this.normalizedLayoutBoundsSpace().Sub(this.scroll)
2023-07-12 05:17:12 +00:00
if previousContentBounds != this.contentBounds {
this.on.contentBoundsChange.Broadcast()
}
2023-07-02 06:52:14 +00:00
}