components_ui_button_TextButton.bs

import "pkg:/source/utils/misc.bs"

sub init()
  m.buttonBackground = m.top.findNode("buttonBackground")
  m.buttonBorder = m.top.findNode("buttonBorder")
  m.buttonText = m.top.findNode("buttonText")

  m.top.observeField("enabled", "onEnabledChanged")
  m.top.observeField("background", "onBackgroundChanged")
  m.top.observeField("textColor", "onTextColorChanged")
  m.top.observeField("text", "onTextChanged")
  m.top.observeField("padding", "onPaddingChanged")
  m.top.observeField("focusedChild", "onFocusChanged")
  m.top.observeField("minWidth", "onSizeChanged")
  m.top.observeField("minHeight", "onSizeChanged")

  ' Enable render tracking on parent to know when component has rendered
  m.top.enableRenderTracking = true
  m.top.observeField("renderTracking", "onRenderComplete")

  applyTheme()
end sub

sub applyTheme()
  constants = m.global.constants

  ' Set default colors
  m.top.background = constants.colorBackgroundSecondary
  m.top.textColor = constants.colorTextPrimary
  m.top.focusBorder = constants.colorPrimary
  m.top.focusBackground = constants.colorBackgroundSecondary
  m.top.textFocusColor = constants.colorTextPrimary
end sub

sub onFocusChanged()
  if m.top.hasFocus()
    ' Button border - change to focus color
    if m.top.enableBorder
      m.buttonBorder.blendColor = m.top.focusBorder
    else
      ' Border disabled - ensure it stays invisible
      m.buttonBorder.blendColor = m.top.background
    end if
    ' Button background
    m.buttonBackground.blendColor = m.top.focusBackground
    ' Button text
    m.buttonText.color = m.top.textFocusColor
  else
    ' Button border - match background color
    m.buttonBorder.blendColor = m.top.background
    ' Button background
    m.buttonBackground.blendColor = m.top.background
    ' Button text
    m.buttonText.color = m.top.textColor
  end if
end sub

sub onBackgroundChanged()
  m.buttonBackground.blendColor = m.top.background
  ' Border matches background when not focused OR when borders are disabled
  if not m.top.hasFocus() or not m.top.enableBorder
    m.buttonBorder.blendColor = m.top.background
  end if
end sub

sub onTextColorChanged()
  m.buttonText.color = m.top.textColor
end sub

sub onTextChanged()
  m.buttonText.text = m.top.text
  ' If already rendered, resize immediately. Otherwise wait for onRenderComplete
  if m.top.renderTracking = "full"
    setSizeAndCenterText()
  end if
end sub

' Called when the component has rendered and we can measure the label accurately
sub onRenderComplete()
  if m.top.renderTracking <> "full" then return
  setSizeAndCenterText()
end sub

sub setSizeAndCenterText()
  if not isValid(m.buttonText) then return
  if m.buttonText.text.Len() = 0 then return

  ' Reset label size to allow accurate measurement of text content
  m.buttonText.width = 0
  m.buttonText.height = 0

  ' Get accurate text dimensions from rendered label
  boundingRect = m.buttonText.localBoundingRect()
  textWidth = boundingRect.width

  ' If not rendered yet, wait for onRenderComplete
  if textWidth = 0 then return

  ' Calculate visible text height for equal padding
  ' localBoundingRect height includes line spacing, adjust to match horizontal padding
  fontSize = m.buttonText.font.size
  visibleTextHeight = fontSize * 0.7

  ' Calculate button dimensions with padding
  buttonWidth = textWidth + (m.top.padding * 2)
  buttonHeight = visibleTextHeight + (m.top.padding * 2)

  ' Ensure minimum dimensions (this is the ONLY place minWidth should be enforced)
  if buttonWidth < m.top.minWidth
    buttonWidth = m.top.minWidth
  end if
  if buttonHeight < m.top.minHeight
    buttonHeight = m.top.minHeight
  end if

  ' Set background size
  m.buttonBackground.width = buttonWidth
  m.buttonBackground.height = buttonHeight

  ' Center the text in the button
  m.buttonText.width = buttonWidth
  m.buttonText.height = buttonHeight
  m.buttonText.translation = [0, 0]

  setFocusBorderSize()

  ' Show the button now that it's properly sized
  m.buttonBackground.visible = true
  m.buttonBorder.visible = true
  m.buttonText.visible = true

  ' Signal that the button is ready (sized and visible)
  m.top.ready = true
end sub

sub onPaddingChanged()
  setSizeAndCenterText()
end sub

sub onSizeChanged()
  setSizeAndCenterText()
end sub

sub onHeightChanged()
  setSizeAndCenterText()
end sub

sub onWidthChanged()
  setSizeAndCenterText()
end sub

sub onEnabledChanged()
  constants = m.global.constants

  if m.top.enabled
    m.top.background = constants.colorBackgroundSecondary
    m.top.textColor = constants.colorTextPrimary
  else
    m.top.background = constants.colorBackgroundPrimary
    m.top.textColor = constants.colorTextDisabled
  end if
end sub

sub setFocusBorderSize()
  if m.buttonBackground.width < 1 then return

  ' Border poster must be same size as background
  m.buttonBorder.width = m.buttonBackground.width
  m.buttonBorder.height = m.buttonBackground.height
end sub