Update internal system
This commit is contained in:
parent
e2b3b84993
commit
2af42a3568
@ -1,26 +1,48 @@
|
||||
package system
|
||||
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
import "git.tebibyte.media/tomo/backend/internal/util"
|
||||
|
||||
type attrHierarchy [T tomo.Attr] struct {
|
||||
style T
|
||||
user T
|
||||
userExists bool
|
||||
fallback T
|
||||
style util.Optional[T]
|
||||
user util.Optional[T]
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) SetFallback (fallback T) {
|
||||
this.fallback = fallback
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) SetStyle (style T) (different bool) {
|
||||
styleEquals := this.style.Equals(style)
|
||||
this.style = style
|
||||
return !styleEquals && !this.userExists
|
||||
styleEquals := false
|
||||
if previous, ok := this.style.Value(); ok {
|
||||
styleEquals = previous.Equals(style)
|
||||
}
|
||||
this.style.Set(style)
|
||||
return !styleEquals && !this.user.Exists()
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) UnsetStyle () (different bool) {
|
||||
different = this.style.Exists()
|
||||
this.style.Unset()
|
||||
return different
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) SetUser (user T) (different bool) {
|
||||
userEquals := this.user.Equals(user)
|
||||
this.user = user
|
||||
this.userExists = true
|
||||
userEquals := false
|
||||
if previous, ok := this.user.Value(); ok {
|
||||
userEquals = previous.Equals(user)
|
||||
}
|
||||
this.user.Set(user)
|
||||
return !userEquals
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) UnsetUser () (different bool) {
|
||||
different = this.user.Exists()
|
||||
this.user.Unset()
|
||||
return different
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) Set (attr T, user bool) (different bool) {
|
||||
if user {
|
||||
return this.SetUser(attr)
|
||||
@ -29,10 +51,20 @@ func (this *attrHierarchy[T]) Set (attr T, user bool) (different bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) Value () T {
|
||||
if this.userExists {
|
||||
return this.user
|
||||
func (this *attrHierarchy[T]) Unset (user bool) (different bool) {
|
||||
if user {
|
||||
return this.UnsetUser()
|
||||
} else {
|
||||
return this.style
|
||||
return this.UnsetStyle()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *attrHierarchy[T]) Value () T {
|
||||
if user, ok := this.user.Value(); ok {
|
||||
return user
|
||||
} else if style, ok := this.style.Value(); ok{
|
||||
return style
|
||||
} else {
|
||||
return this.fallback
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,11 @@ type box struct {
|
||||
parent parent
|
||||
outer anyBox
|
||||
|
||||
tags util.Set[string]
|
||||
role tomo.Role
|
||||
lastStyleNonce int
|
||||
lastIconsNonce int
|
||||
styleApplicator *styleApplicator
|
||||
tags util.Set[string]
|
||||
role tomo.Role
|
||||
lastStyleNonce int
|
||||
lastIconSetNonce int
|
||||
styleApplicator *styleApplicator
|
||||
|
||||
minSize util.Memo[image.Point]
|
||||
bounds image.Rectangle
|
||||
@ -41,7 +41,6 @@ type box struct {
|
||||
focused bool
|
||||
pressed bool
|
||||
|
||||
|
||||
canvas util.Memo[canvas.Canvas]
|
||||
drawer canvas.Drawer
|
||||
|
||||
@ -71,6 +70,7 @@ func (this *System) newBox (outer anyBox) *box {
|
||||
drawer: outer,
|
||||
tags: make(util.Set[string]),
|
||||
}
|
||||
box.attrColor.SetFallback(tomo.AColor(color.Transparent))
|
||||
box.canvas = util.NewMemo (func () canvas.Canvas {
|
||||
if box.parent == nil { return nil }
|
||||
parentCanvas := box.parent.getCanvas()
|
||||
@ -141,6 +141,10 @@ func (this *box) SetAttr (attr tomo.Attr) {
|
||||
this.outer.setAttr(attr, true)
|
||||
}
|
||||
|
||||
func (this *box) UnsetAttr (kind tomo.AttrKind) {
|
||||
this.outer.unsetAttr(kind, true)
|
||||
}
|
||||
|
||||
func (this *box) SetDNDData (dat data.Data) {
|
||||
this.dndData = dat
|
||||
}
|
||||
@ -201,18 +205,7 @@ func (this *box) setAttr (attr tomo.Attr, user bool) {
|
||||
case tomo.AttrBorder:
|
||||
previousBorderSum := this.borderSum()
|
||||
different := this.attrBorder.Set(attr, user)
|
||||
|
||||
// only invalidate the layout if the border is sized differently
|
||||
if this.borderSum() != previousBorderSum {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
|
||||
// if the border takes up the same amount of space, only invalidate the
|
||||
// drawing if it looks different
|
||||
if different {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
this.handleBorderChange(previousBorderSum, different)
|
||||
|
||||
case tomo.AttrMinimumSize:
|
||||
if this.attrMinimumSize.Set(attr, user) {
|
||||
@ -220,7 +213,42 @@ func (this *box) setAttr (attr tomo.Attr, user bool) {
|
||||
}
|
||||
|
||||
case tomo.AttrPadding:
|
||||
if this.attrPadding.Set(attr, true) {
|
||||
if this.attrPadding.Set(attr, user) {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *box) unsetAttr (kind tomo.AttrKind, user bool) {
|
||||
switch kind {
|
||||
case tomo.AttrKindColor:
|
||||
if this.attrColor.Unset(user) {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindTexture:
|
||||
if this.attrTexture.Unset(user) {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindTextureMode:
|
||||
if this.attrTextureMode.Unset(user) {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindBorder:
|
||||
previousBorderSum := this.borderSum()
|
||||
different := this.attrBorder.Unset(user)
|
||||
this.handleBorderChange(previousBorderSum, different)
|
||||
|
||||
case tomo.AttrKindMinimumSize:
|
||||
if this.attrMinimumSize.Unset(user) {
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
|
||||
case tomo.AttrKindPadding:
|
||||
if this.attrPadding.Unset(user) {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
@ -531,7 +559,21 @@ func (this *box) recursiveRedo () {
|
||||
}
|
||||
|
||||
func (this *box) recursiveLoseCanvas () {
|
||||
this.canvas.InvalidateTo(nil)
|
||||
this.canvas.Invalidate()
|
||||
}
|
||||
|
||||
func (this *box) handleBorderChange (previousBorderSum tomo.Inset, different bool) {
|
||||
// only invalidate the layout if the border is sized differently
|
||||
if this.borderSum() != previousBorderSum {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
|
||||
// if the border takes up the same amount of space, only invalidate the
|
||||
// drawing if it looks different
|
||||
if different {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *box) invalidateStyle () {
|
||||
@ -578,9 +620,9 @@ func (this *box) recursiveReApply () {
|
||||
}
|
||||
|
||||
// icons
|
||||
hierarchyIconsNonce := this.getIconsNonce()
|
||||
if this.lastIconsNonce != hierarchyIconsNonce {
|
||||
this.lastIconsNonce = hierarchyIconsNonce
|
||||
hierarchyIconSetNonce := this.getIconSetNonce()
|
||||
if this.lastIconSetNonce != hierarchyIconSetNonce {
|
||||
this.lastIconSetNonce = hierarchyIconSetNonce
|
||||
this.on.iconSetChange.Broadcast()
|
||||
}
|
||||
}
|
||||
@ -629,7 +671,7 @@ func (this *box) getStyleNonce () int {
|
||||
return this.getHierarchy().getStyleNonce()
|
||||
}
|
||||
|
||||
func (this *box) getIconsNonce () int {
|
||||
func (this *box) getIconSetNonce () int {
|
||||
// should panic if not in the tree
|
||||
return this.getHierarchy().getIconsNonce()
|
||||
return this.getHierarchy().getIconSetNonce()
|
||||
}
|
||||
|
@ -186,6 +186,53 @@ func (this *containerBox) setAttr (attr tomo.Attr, user bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *containerBox) unsetAttr (kind tomo.AttrKind, user bool) {
|
||||
switch kind {
|
||||
case tomo.AttrKindColor:
|
||||
if this.attrColor.Unset(user) {
|
||||
this.invalidateTransparentChildren()
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindTexture:
|
||||
if this.attrTexture.Unset(user) {
|
||||
this.invalidateTransparentChildren()
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindTextureMode:
|
||||
if this.attrTextureMode.Unset(user) {
|
||||
this.invalidateTransparentChildren()
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindGap:
|
||||
if this.attrGap.Unset(user) {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
|
||||
case tomo.AttrKindAlign:
|
||||
if this.attrAlign.Unset(user) {
|
||||
this.invalidateLayout()
|
||||
}
|
||||
|
||||
case tomo.AttrKindOverflow:
|
||||
if this.attrOverflow.Unset(user) {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
|
||||
case tomo.AttrKindLayout:
|
||||
if this.attrLayout.Unset(user) {
|
||||
this.invalidateLayout()
|
||||
this.invalidateMinimum()
|
||||
}
|
||||
|
||||
default: this.box.unsetAttr(kind, user)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *containerBox) recommendedHeight (width int) int {
|
||||
layout := this.attrLayout.Value().Layout
|
||||
if layout == nil || !this.attrOverflow.Value().Y {
|
||||
|
@ -4,6 +4,7 @@ import "image"
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
import "git.tebibyte.media/tomo/tomo/input"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
import "git.tebibyte.media/tomo/backend/style"
|
||||
import "git.tebibyte.media/tomo/backend/internal/util"
|
||||
|
||||
// Hierarchy is coupled to a tomo.Window implementation, and manages a tree of
|
||||
@ -168,16 +169,24 @@ func (this *Hierarchy) getWindow () tomo.Window {
|
||||
return this.link.GetWindow()
|
||||
}
|
||||
|
||||
func (this *Hierarchy) getStyle () *tomo.Style {
|
||||
func (this *Hierarchy) getStyle () *style.Style {
|
||||
return this.system.style
|
||||
}
|
||||
|
||||
func (this *Hierarchy) getIconSet () style.IconSet {
|
||||
return this.system.iconSet
|
||||
}
|
||||
|
||||
func (this *Hierarchy) getFaceSet () style.FaceSet {
|
||||
return this.system.faceSet
|
||||
}
|
||||
|
||||
func (this *Hierarchy) getStyleNonce () int {
|
||||
return this.system.styleNonce
|
||||
}
|
||||
|
||||
func (this *Hierarchy) getIconsNonce () int {
|
||||
return this.system.iconsNonce
|
||||
func (this *Hierarchy) getIconSetNonce () int {
|
||||
return this.system.iconSetNonce
|
||||
}
|
||||
|
||||
func (this *Hierarchy) getCanvas () canvas.Canvas {
|
||||
|
@ -75,6 +75,9 @@ type anyBox interface {
|
||||
// setAttr sets an attribute at the user or style level depending
|
||||
// on the value of user.
|
||||
setAttr (attr tomo.Attr, user bool)
|
||||
// unsetAttr unsets an attribute at the user or style level depending
|
||||
// on the value of user.
|
||||
unsetAttr (kind tomo.AttrKind, user bool)
|
||||
|
||||
// propagate recursively calls a function on this anyBox, and all of its
|
||||
// children (if applicable) The normal propagate behavior calls the
|
||||
|
@ -1,11 +1,12 @@
|
||||
package system
|
||||
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
import "git.tebibyte.media/tomo/backend/style"
|
||||
|
||||
type styleApplicator struct {
|
||||
style *tomo.Style
|
||||
style *style.Style
|
||||
role tomo.Role
|
||||
rules []tomo.Rule
|
||||
rules []style.Rule
|
||||
}
|
||||
|
||||
func (this *styleApplicator) apply (box anyBox) {
|
||||
@ -25,7 +26,7 @@ func (this *styleApplicator) apply (box anyBox) {
|
||||
}
|
||||
|
||||
// compile list of attributes by searching through the cached ruleset
|
||||
attrs := make(tomo.AttrSet)
|
||||
attrs := make(style.AttrSet)
|
||||
for _, rule := range this.rules {
|
||||
satisifed := true
|
||||
for _, tag := range rule.Tags {
|
||||
|
@ -2,8 +2,8 @@ package system
|
||||
|
||||
import "io"
|
||||
import "image"
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
import "git.tebibyte.media/tomo/backend/style"
|
||||
import "git.tebibyte.media/tomo/backend/internal/util"
|
||||
|
||||
// System is coupled to a tomo.Backend implementation, and manages Hierarchies
|
||||
@ -11,9 +11,11 @@ import "git.tebibyte.media/tomo/backend/internal/util"
|
||||
type System struct {
|
||||
link BackendLink
|
||||
|
||||
style *tomo.Style
|
||||
styleNonce int
|
||||
iconsNonce int
|
||||
style *style.Style
|
||||
iconSet style.IconSet
|
||||
faceSet style.FaceSet
|
||||
styleNonce int
|
||||
iconSetNonce int
|
||||
|
||||
hierarchies util.Set[*Hierarchy]
|
||||
}
|
||||
@ -43,9 +45,9 @@ func New (link BackendLink) *System {
|
||||
}
|
||||
}
|
||||
|
||||
// SetStyle sets the tomo.Style that is applied to objects, and notifies them
|
||||
// SetStyle sets the style that is applied to objects, and notifies them
|
||||
// that the style has changed.
|
||||
func (this *System) SetStyle (style *tomo.Style) {
|
||||
func (this *System) SetStyle (style *style.Style) {
|
||||
this.style = style
|
||||
this.styleNonce ++
|
||||
for hierarchy := range this.hierarchies {
|
||||
@ -53,14 +55,21 @@ func (this *System) SetStyle (style *tomo.Style) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetIconSet notifies objects that the icons have changed.
|
||||
func (this *System) SetIconSet (iconSet tomo.IconSet) {
|
||||
this.iconsNonce ++
|
||||
// SetIconSet sets the icon set that provides icon textures, and notifies
|
||||
// objects that the icons have changed.
|
||||
func (this *System) SetIconSet (iconSet style.IconSet) {
|
||||
this.iconSet = iconSet
|
||||
this.iconSetNonce ++
|
||||
for hierarchy := range this.hierarchies {
|
||||
hierarchy.setIconSet()
|
||||
}
|
||||
}
|
||||
|
||||
// SetFaceSet sets the face set that provides font faces.
|
||||
func (this *System) SetFaceSet (faceSet style.FaceSet) {
|
||||
this.faceSet = faceSet
|
||||
}
|
||||
|
||||
func (this *System) removeHierarchy (hierarchy *Hierarchy) {
|
||||
delete(this.hierarchies, hierarchy)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package system
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "git.tebibyte.media/tomo/tomo"
|
||||
import "golang.org/x/image/math/fixed"
|
||||
import "git.tebibyte.media/tomo/typeset"
|
||||
@ -9,6 +10,7 @@ import "git.tebibyte.media/tomo/tomo/text"
|
||||
import "git.tebibyte.media/tomo/tomo/input"
|
||||
import "git.tebibyte.media/tomo/tomo/event"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
import "git.tebibyte.media/tomo/backend/internal/util"
|
||||
|
||||
type textBox struct {
|
||||
*box
|
||||
@ -31,6 +33,7 @@ type textBox struct {
|
||||
dot text.Dot
|
||||
|
||||
drawer typeset.Drawer
|
||||
face util.Cycler[font.Face]
|
||||
|
||||
on struct {
|
||||
contentBoundsChange event.FuncBroadcaster
|
||||
@ -41,6 +44,8 @@ type textBox struct {
|
||||
func (this *System) NewTextBox () tomo.TextBox {
|
||||
box := &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}))
|
||||
return box
|
||||
}
|
||||
|
||||
@ -106,9 +111,8 @@ func (this *textBox) OnDotChange (callback func ()) event.Cookie {
|
||||
func (this *textBox) Draw (can canvas.Canvas) {
|
||||
if can == nil { return }
|
||||
|
||||
texture := this.attrTexture.Value().Texture
|
||||
col := this.attrColor.Value().Color
|
||||
if col == nil { col = color.Transparent }
|
||||
texture := this.attrTexture.Value().Texture
|
||||
col := this.attrColor.Value().Color
|
||||
|
||||
this.drawBorders(can)
|
||||
|
||||
@ -124,9 +128,8 @@ func (this *textBox) Draw (can canvas.Canvas) {
|
||||
this.drawDot(can)
|
||||
}
|
||||
|
||||
if this.attrFace.Value().Face != nil {
|
||||
if this.face.Value() != nil {
|
||||
textColor := this.attrTextColor.Value().Color
|
||||
if textColor == nil { textColor = color.Black }
|
||||
this.drawer.Draw(can, textColor, this.textOffset())
|
||||
}
|
||||
}
|
||||
@ -145,23 +148,22 @@ func (this *textBox) setAttr (attr tomo.Attr, user bool) {
|
||||
|
||||
case tomo.AttrFace:
|
||||
if this.attrFace.Set(attr, user) {
|
||||
this.drawer.SetFace(attr.Face)
|
||||
this.invalidateMinimum()
|
||||
this.invalidateLayout()
|
||||
this.handleFaceChange()
|
||||
}
|
||||
|
||||
case tomo.AttrWrap:
|
||||
if this.attrWrap.Set(attr, user) {
|
||||
this.drawer.SetWrap(bool(attr))
|
||||
this.drawer.SetWrap(bool(this.attrWrap.Value()))
|
||||
this.invalidateMinimum()
|
||||
this.invalidateLayout()
|
||||
}
|
||||
|
||||
case tomo.AttrAlign:
|
||||
if this.attrAlign.Set(attr, user) {
|
||||
align := this.attrAlign.Value()
|
||||
this.drawer.SetAlign (
|
||||
typeset.Align(attr.X),
|
||||
typeset.Align(attr.Y))
|
||||
typeset.Align(align.X),
|
||||
typeset.Align(align.Y))
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
@ -175,6 +177,49 @@ func (this *textBox) setAttr (attr tomo.Attr, user bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *textBox) unsetAttr (kind tomo.AttrKind, user bool) {
|
||||
switch kind {
|
||||
case tomo.AttrKindTextColor:
|
||||
if this.attrTextColor.Unset(user) && !this.dot.Empty() {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindDotColor:
|
||||
if this.attrDotColor.Unset(user) && !this.dot.Empty() {
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindFace:
|
||||
if this.attrFace.Unset(user) {
|
||||
this.handleFaceChange()
|
||||
}
|
||||
|
||||
case tomo.AttrKindWrap:
|
||||
if this.attrWrap.Unset(user) {
|
||||
this.drawer.SetWrap(bool(this.attrWrap.Value()))
|
||||
this.invalidateMinimum()
|
||||
this.invalidateLayout()
|
||||
}
|
||||
|
||||
case tomo.AttrKindAlign:
|
||||
if this.attrAlign.Unset(user) {
|
||||
align := this.attrAlign.Value()
|
||||
this.drawer.SetAlign (
|
||||
typeset.Align(align.X),
|
||||
typeset.Align(align.Y))
|
||||
this.invalidateDraw()
|
||||
}
|
||||
|
||||
case tomo.AttrKindOverflow:
|
||||
if this.attrOverflow.Unset(user) {
|
||||
this.invalidateMinimum()
|
||||
this.invalidateLayout()
|
||||
}
|
||||
|
||||
default: this.box.unsetAttr(kind, user)
|
||||
}
|
||||
}
|
||||
|
||||
func roundPt (point fixed.Point26_6) image.Point {
|
||||
return image.Pt(point.X.Round(), point.Y.Round())
|
||||
}
|
||||
@ -184,13 +229,11 @@ func fixPt (point image.Point) fixed.Point26_6 {
|
||||
}
|
||||
|
||||
func (this *textBox) drawDot (can canvas.Canvas) {
|
||||
if this.attrFace.Value().Face == nil { return }
|
||||
face := this.face.Value()
|
||||
if face == nil { return }
|
||||
|
||||
face := this.attrFace.Value().Face
|
||||
textColor := this.attrTextColor.Value().Color
|
||||
dotColor := this.attrDotColor.Value().Color
|
||||
if textColor == nil { textColor = color.Black }
|
||||
if dotColor == nil { dotColor = color.RGBA { G: 255, B: 255, A: 255 } }
|
||||
|
||||
pen := can.Pen()
|
||||
|
||||
@ -447,3 +490,16 @@ func (this *textBox) scrollToDot () {
|
||||
|
||||
this.ScrollTo(scroll)
|
||||
}
|
||||
|
||||
func (this *textBox) handleFaceChange () {
|
||||
hierarchy := this.getHierarchy()
|
||||
if hierarchy != nil { return }
|
||||
faceSet := hierarchy.getFaceSet()
|
||||
if faceSet != nil { return }
|
||||
|
||||
face := faceSet.Face(tomo.Face(this.attrFace.Value()))
|
||||
this.face.Set(face, face)
|
||||
this.drawer.SetFace(face)
|
||||
this.invalidateMinimum()
|
||||
this.invalidateLayout()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user