Skip to content

Commit

Permalink
resolve issues with size changes to label and background
Browse files Browse the repository at this point in the history
  • Loading branch information
mggower committed Nov 6, 2023
1 parent 04d8f93 commit ab652c6
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 80 deletions.
8 changes: 4 additions & 4 deletions examples/native/src/labels/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const NODE_STYLE: Graph.NodeStyle = {
icon: TEXT_ICON,
stroke: [{ width: 2, color: GREEN_LIGHT }],
label: {
position: 'bottom',
position: 'right',
fontName: 'NodeLabel',
fontFamily: ['Arial', 'sans-serif'],
background: { color: GREEN_LIGHT },
Expand All @@ -33,11 +33,11 @@ const NODE_HOVER_STYLE: Graph.NodeStyle = {
icon: TEXT_ICON,
stroke: [{ width: 2, color: GREEN_LIGHT }],
label: {
position: 'bottom',
position: 'right',
fontName: 'NodeLabelHover',
fontFamily: ['Arial', 'sans-serif'],
background: { color: GREEN_LIGHT },
color: DARK_GREEN,
background: { color: DARK_GREEN },
color: '#FFF',
margin: 4
}
}
Expand Down
97 changes: 48 additions & 49 deletions src/renderers/webgl/objects/label/Label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class Label {
mounted = false

private dirty = false
private transformed = false
private label: string
private container: Container
private text: BitmapText | Text
Expand All @@ -35,7 +36,17 @@ export class Label {
}

update(label: string, coords: LabelCoords, style?: LabelStyle) {
const previous = this.text.getLocalBounds()

this.value = label

const isBitmapText = this.isBitmapText()
const isASCII = utils.isASCII(label)
// if the text type has changed, regenerate a new text object
if ((isBitmapText && !isASCII) || (!isBitmapText && isASCII)) {
this.transformText(label, utils.mergeDefaults(style))
}

this.style.margin = style?.margin
this.coordinates = coords
this.wordWrap = style?.wordWrap
Expand All @@ -48,25 +59,19 @@ export class Label {
this.fontName = style?.fontName ?? STYLE_DEFAULTS.FONT_NAME
this.background = style?.background

const isBitmapText = this.isBitmapText()
const isASCII = utils.isASCII(label)

if (isASCII) {
// conditionally load font if BitmapFont is unavailable
utils.loadFont(this.style)
}

if ((isBitmapText && !isASCII) || (!isBitmapText && isASCII)) {
// if the text type has changed, regenerate a new text object
this.dirty = false
this.transformText()
}

if (this.dirty) {
this.dirty = false
this.updateText()
}

if (this.backgroundSprite) {
const bounds = this.text.getLocalBounds()
if (previous.width !== bounds.width || previous.height !== bounds.height) {
utils.setBackgroundSize(this.backgroundSprite, bounds, this.style.background?.padding)
}
}

this.transformed = false
return this
}

Expand Down Expand Up @@ -115,27 +120,17 @@ export class Label {
}
}

private transformText() {
if (this.isBitmapText()) {
const isMounted = this.mounted
this.delete()
this.text = new Text(this.label, utils.getTextStyle(this.style))
this.position = this.style.position
this.x = undefined
this.y = undefined
if (isMounted) {
this.mount()
}
} else {
const isMounted = this.mounted
this.delete()
this.text = new BitmapText(this.label, utils.getBitmapStyle(this.style))
this.position = this.style.position
this.x = undefined
this.y = undefined
if (isMounted) {
this.mount()
}
private transformText(label: string, style: StyleWithDefaults) {
const isMounted = this.mounted

this.delete()
this.text = utils.createTextObject(label, style)
this.transformed = true
this.x = undefined
this.y = undefined

if (isMounted) {
this.mount()
}
}

Expand Down Expand Up @@ -172,7 +167,7 @@ export class Label {
this.align = utils.getPositionAlign(position)
this.anchor = utils.getPositionAnchor(position)
if (position !== this.style.position) {
this.dirty = true
this.dirty = !this.transformed
this.style.position = position
}
}
Expand All @@ -199,20 +194,22 @@ export class Label {

private set fontSize(fontSize: number) {
if (fontSize !== this.style.fontSize) {
this.dirty = true
this.style.fontSize = fontSize
if (this.isBitmapText(this.text)) {
this.text.fontSize = fontSize
} else {
this.text.style.fontSize = fontSize
if (!this.transformed) {
this.dirty = true
if (this.isBitmapText(this.text)) {
this.text.fontSize = fontSize
} else {
this.text.style.fontSize = fontSize
}
}
}
}

private set wordWrap(wordWrap: number | undefined) {
if (wordWrap !== this.style.wordWrap) {
this.style.wordWrap = wordWrap
if (!this.isBitmapText(this.text)) {
if (!this.transformed && !this.isBitmapText(this.text)) {
this.dirty = true
this.text.style.wordWrap = wordWrap !== undefined
this.text.style.wordWrapWidth = wordWrap ?? 0
Expand All @@ -223,7 +220,7 @@ export class Label {
private set color(color: TextStyleFill | undefined) {
if (!equals(color, this.style.color)) {
this.style.color = color
if (!this.isBitmapText(this.text)) {
if (!this.transformed && !this.isBitmapText(this.text)) {
this.dirty = true
this.text.style.fill = color ?? STYLE_DEFAULTS.COLOR
}
Expand All @@ -233,7 +230,7 @@ export class Label {
private set stroke(stroke: Stroke | undefined) {
if (!equals(stroke, this.style.stroke)) {
this.style.stroke = stroke
if (!this.isBitmapText(this.text)) {
if (!this.transformed && !this.isBitmapText(this.text)) {
this.dirty = true
this.text.style.stroke = stroke?.color ?? STYLE_DEFAULTS.STROKE
this.text.style.strokeThickness = stroke?.width ?? STYLE_DEFAULTS.STROKE_THICKNESS
Expand All @@ -243,18 +240,20 @@ export class Label {

private set fontFamily(fontFamily: string | string[]) {
if (!equals(fontFamily, this.style.fontFamily)) {
this.dirty = true
this.style.fontFamily = fontFamily
if (!this.isBitmapText(this.text)) {
this.text.style.fontFamily = fontFamily
if (!this.transformed) {
this.dirty = true
if (!this.isBitmapText(this.text)) {
this.text.style.fontFamily = fontFamily
}
}
}
}

private set fontName(fontName: string) {
if (fontName !== this.style.fontName) {
this.style.fontName = fontName
if (this.isBitmapText(this.text)) {
if (!this.transformed && this.isBitmapText(this.text)) {
this.dirty = true
this.text.fontName = fontName
}
Expand All @@ -264,7 +263,7 @@ export class Label {
private set fontWeight(fontWeight: TextStyleFontWeight | undefined) {
if (fontWeight !== this.style.fontWeight) {
this.style.fontWeight = fontWeight
if (!this.isBitmapText(this.text)) {
if (!this.transformed && !this.isBitmapText(this.text)) {
this.dirty = true
this.text.style.fontWeight = fontWeight ?? STYLE_DEFAULTS.FONT_WEIGHT
}
Expand Down
75 changes: 48 additions & 27 deletions src/renderers/webgl/objects/label/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { Stroke } from '../../../../types'
import {
Text,
Expand All @@ -12,7 +11,8 @@ import {
Sprite,
Texture,
BitmapText,
TextStyleFontWeight
TextStyleFontWeight,
Rectangle
} from 'pixi.js'

export type LabelPosition = 'bottom' | 'left' | 'top' | 'right'
Expand Down Expand Up @@ -53,8 +53,9 @@ const RESOLUTION = 2
export const STYLE_DEFAULTS = {
FONT_SIZE: 10,
STROKE_THICKNESS: 0,
PADDING: [4, 8] as [number, number],
LETTER_SPACING: 0.5,
WORD_WRAP: false,
PADDING: [4, 8] as [number, number],
STROKE: '#FFF',
FONT_NAME: 'Label',
COLOR: '#000000',
Expand All @@ -77,7 +78,8 @@ TextStyle.defaultStyle = {
wordWrap: STYLE_DEFAULTS.WORD_WRAP,
fontSize: STYLE_DEFAULTS.FONT_SIZE,
fontFamily: STYLE_DEFAULTS.FONT_FAMILY,
strokeThickness: STYLE_DEFAULTS.STROKE_THICKNESS
strokeThickness: STYLE_DEFAULTS.STROKE_THICKNESS,
letterSpacing: STYLE_DEFAULTS.LETTER_SPACING
}

// utils
Expand Down Expand Up @@ -156,31 +158,19 @@ const getTextStyle = ({ color, fontFamily, fontSize, fontWeight, wordWrap, strok
const getBitmapStyle = (style: StyleWithDefaults): Partial<IBitmapTextStyle> => ({
fontName: style.fontName,
fontSize: style.fontSize,
align: getPositionAlign(style.position)
align: getPositionAlign(style.position),
letterSpacing: style.letterSpacing ?? STYLE_DEFAULTS.LETTER_SPACING
})

const bitmapFontIsAvailable = (fontName: string) => BitmapFont.available[fontName] !== undefined

const loadFont = (style: StyleWithDefaults) => {
if (!bitmapFontIsAvailable(style.fontName)) {
BitmapFont.from(style.fontName, { ...getTextStyle(style), letterSpacing: 1 }, { resolution: RESOLUTION, chars: BitmapFont.ASCII })
if (BitmapFont.available[style.fontName] === undefined) {
BitmapFont.from(style.fontName, getTextStyle(style), {
resolution: RESOLUTION,
chars: BitmapFont.ASCII
})
}
}

const getBackgroundPadding = (padding: BackgroundPadding = STYLE_DEFAULTS.PADDING): [vertical: number, horizontal: number] => {
return typeof padding === 'number' ? [padding, padding] : padding
}

const setBackgroundStyle = (sprite: Sprite, text: Text | BitmapText, { color, opacity = 1, padding }: LabelBackgroundStyle) => {
const [vertical, horizontal] = getBackgroundPadding(padding)
sprite.anchor.set(text.anchor.x, text.anchor.y)
sprite.height = text.height + vertical
sprite.width = text.width + horizontal
sprite.alpha = opacity
sprite.tint = color
return sprite
}

const createTextObject = (label: string, style: StyleWithDefaults) => {
let text: BitmapText | Text

Expand All @@ -195,6 +185,24 @@ const createTextObject = (label: string, style: StyleWithDefaults) => {
return text
}

const getBackgroundPadding = (padding: BackgroundPadding = STYLE_DEFAULTS.PADDING): [vertical: number, horizontal: number] => {
return typeof padding === 'number' ? [padding, padding] : padding
}

const setBackgroundSize = (sprite: Sprite, bounds: Rectangle, padding?: BackgroundPadding) => {
const [vertical, horizontal] = getBackgroundPadding(padding)
sprite.height = bounds.height + vertical
sprite.width = bounds.width + horizontal
return sprite
}

const setBackgroundStyle = (sprite: Sprite, text: Text | BitmapText, { color, opacity = 1, padding }: LabelBackgroundStyle) => {
sprite.anchor.set(text.anchor.x, text.anchor.y)
sprite.alpha = opacity
sprite.tint = color
return setBackgroundSize(sprite, text.getLocalBounds(), padding)
}

const createBackgroundSprite = (text: Text | BitmapText, style: LabelBackgroundStyle) => {
const sprite = Sprite.from(Texture.WHITE, { resolution: RESOLUTION })
return setBackgroundStyle(sprite, text, style)
Expand All @@ -206,9 +214,8 @@ const getLabelCoordinates = (
isBitmapText: boolean
) => {
const shift = margin + offset
// BitmapText shifts text down 2px
const label = { x, y: isBitmapText ? y - 1 : y + 1 }
const bg = { x, y: isBitmapText ? y - 1 : y + 1 }
const label = { x, y }
const bg = { x, y }

let vertical = 0
let horizontal = 0
Expand All @@ -222,18 +229,32 @@ const getLabelCoordinates = (
case 'bottom':
label.y += shift + vertical
bg.y += shift

break
case 'left':
label.x -= shift + horizontal
bg.x -= shift

if (isBitmapText) {
label.y -= 1
bg.y -= 1
}

break
case 'top':
label.y -= shift + vertical
bg.y -= shift

break
case 'right':
label.x += shift + horizontal
bg.x += shift

if (isBitmapText) {
label.y -= 1
bg.y -= 1
}

break
}

Expand All @@ -248,9 +269,9 @@ export default {
getPositionAnchor,
getTextStyle,
getBitmapStyle,
bitmapFontIsAvailable,
loadFont,
createTextObject,
setBackgroundSize,
setBackgroundStyle,
createBackgroundSprite,
getBackgroundPadding
Expand Down

0 comments on commit ab652c6

Please sign in to comment.