Fix non-square consent checkbox with a dedicated Panel square

Root cause: CheckBox/Button reserves vertical space for a text
baseline even with an empty string, so no amount of custom_minimum_size
or stylebox content-margin tweaking produced a true square - it just
came out close-but-not-quite in some layouts and visibly elongated in
others.

Decoupled the visual square from the CheckBox entirely: a plain Panel
(exactly 20x20, toggled by direct click) now draws the border/fill,
while a real (invisible) CheckBox still holds .button_pressed for the
rest of the code to read. Also added left padding on the row and
vertically centered both the square and the consent text within it.

Verified with a direct size dump (20x20 in both checked/unchecked
states, where it previously measured non-square) and rendered
screenshots of both states.
This commit is contained in:
hz
2026-07-25 10:45:45 -05:00
parent bff16cea98
commit eb9e018339
+44 -19
View File
@@ -27,6 +27,7 @@ var login_input: LineEdit
var password_input: LineEdit var password_input: LineEdit
var password_reveal_entries: Array[Dictionary] = [] var password_reveal_entries: Array[Dictionary] = []
var legal_consent_texts: Array[RichTextLabel] = [] var legal_consent_texts: Array[RichTextLabel] = []
var legal_consent_squares: Array[Panel] = []
var registration_username_input: LineEdit var registration_username_input: LineEdit
var registration_email_input: LineEdit var registration_email_input: LineEdit
var registration_password_input: LineEdit var registration_password_input: LineEdit
@@ -351,25 +352,47 @@ func _add_legal_consent(parent: VBoxContainer) -> CheckBox:
# wrapping then stays confined to its column, and "Terms of Service"/ # wrapping then stays confined to its column, and "Terms of Service"/
# "Privacy Policy" render as inline links rather than a duplicate row # "Privacy Policy" render as inline links rather than a duplicate row
# of link buttons underneath. # of link buttons underneath.
#
# The actual toggle state lives on a real CheckBox (kept invisible and
# out of the row's layout) because other code reads .button_pressed -
# but Button/CheckBox reserves vertical space for a text baseline even
# with an empty string, which was making the visible square non-square
# no matter what custom_minimum_size said. Drawing the visible square
# as a plain Panel sidesteps that entirely.
var check := CheckBox.new()
check.visible = false
parent.add_child(check)
var margin := MarginContainer.new() var margin := MarginContainer.new()
margin.add_theme_constant_override("margin_left", 6)
margin.add_theme_constant_override("margin_top", 8) margin.add_theme_constant_override("margin_top", 8)
margin.add_theme_constant_override("margin_bottom", 8) margin.add_theme_constant_override("margin_bottom", 8)
parent.add_child(margin) parent.add_child(margin)
var row := HBoxContainer.new() var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 12) row.add_theme_constant_override("separation", 12)
margin.add_child(row) margin.add_child(row)
var check := CheckBox.new()
check.name = "LegalConsentCheckBox" var square := Panel.new()
check.custom_minimum_size = Vector2(22, 22) square.custom_minimum_size = Vector2(20, 20)
check.size_flags_vertical = Control.SIZE_SHRINK_BEGIN square.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
check.add_theme_icon_override("unchecked", _blank_icon()) square.size_flags_vertical = Control.SIZE_SHRINK_CENTER
row.add_child(check) square.mouse_filter = Control.MOUSE_FILTER_STOP
square.set_meta("legal_consent_checkbox", check)
square.gui_input.connect(func(event: InputEvent):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
check.button_pressed = not check.button_pressed
)
row.add_child(square)
legal_consent_squares.append(square)
check.toggled.connect(func(_pressed: bool): _refresh_legal_consent_square(square))
var consent_text := RichTextLabel.new() var consent_text := RichTextLabel.new()
consent_text.bbcode_enabled = true consent_text.bbcode_enabled = true
consent_text.fit_content = true consent_text.fit_content = true
consent_text.scroll_active = false consent_text.scroll_active = false
consent_text.meta_underlined = false consent_text.meta_underlined = false
consent_text.size_flags_horizontal = Control.SIZE_EXPAND_FILL consent_text.size_flags_horizontal = Control.SIZE_EXPAND_FILL
consent_text.size_flags_vertical = Control.SIZE_SHRINK_CENTER
consent_text.add_theme_font_override("normal_font", Brand.font) consent_text.add_theme_font_override("normal_font", Brand.font)
consent_text.add_theme_font_size_override("normal_font_size", 13) consent_text.add_theme_font_size_override("normal_font_size", 13)
consent_text.meta_clicked.connect(func(meta: Variant): _open_legal_document(str(meta))) consent_text.meta_clicked.connect(func(meta: Variant): _open_legal_document(str(meta)))
@@ -378,10 +401,6 @@ func _add_legal_consent(parent: VBoxContainer) -> CheckBox:
return check return check
func _blank_icon() -> ImageTexture:
return ImageTexture.create_from_image(Image.create(1, 1, false, Image.FORMAT_RGBA8))
func _refresh_legal_consent_text(consent_text: RichTextLabel, dark: bool) -> void: func _refresh_legal_consent_text(consent_text: RichTextLabel, dark: bool) -> void:
var text_hex := Brand.text_color(dark).to_html(false) var text_hex := Brand.text_color(dark).to_html(false)
var link_hex := (Brand.SKY_BLUE if dark else Brand.ROYAL_BLUE).to_html(false) var link_hex := (Brand.SKY_BLUE if dark else Brand.ROYAL_BLUE).to_html(false)
@@ -391,6 +410,19 @@ func _refresh_legal_consent_text(consent_text: RichTextLabel, dark: bool) -> voi
) )
func _refresh_legal_consent_square(square: Panel) -> void:
var dark := UIThemeService.is_dark_mode()
var accent := Brand.SKY_BLUE if dark else Brand.ROYAL_BLUE
var checked := square.get_meta("legal_consent_checkbox", null) as CheckBox
var is_checked := checked != null and checked.button_pressed
var box := StyleBoxFlat.new()
box.bg_color = accent if is_checked else Color.TRANSPARENT
box.border_color = accent
box.set_border_width_all(2)
box.set_corner_radius_all(5)
square.add_theme_stylebox_override("panel", box)
func _add_legal_links(parent: HBoxContainer) -> void: func _add_legal_links(parent: HBoxContainer) -> void:
for spec in [["Terms of Service", "terms"], ["Privacy Policy", "privacy"]]: for spec in [["Terms of Service", "terms"], ["Privacy Policy", "privacy"]]:
var link := Button.new() var link := Button.new()
@@ -597,15 +629,8 @@ func _apply_theme() -> void:
_style_auth_field(input, dark) _style_auth_field(input, dark)
for consent_text in legal_consent_texts: for consent_text in legal_consent_texts:
_refresh_legal_consent_text(consent_text, dark) _refresh_legal_consent_text(consent_text, dark)
var checkbox_box := StyleBoxFlat.new() for square in legal_consent_squares:
checkbox_box.bg_color = Color.TRANSPARENT _refresh_legal_consent_square(square)
checkbox_box.border_color = Brand.SKY_BLUE if dark else Brand.ROYAL_BLUE
checkbox_box.set_border_width_all(2)
checkbox_box.set_corner_radius_all(6)
checkbox_box.set_content_margin_all(0)
for cb in find_children("*", "CheckBox", true, false):
for state in ["normal", "hover", "pressed", "focus"]:
cb.add_theme_stylebox_override(state, checkbox_box)
for button in find_children("*", "Button", true, false): for button in find_children("*", "Button", true, false):
if button is CheckBox: if button is CheckBox:
continue continue