Summary
On macOS, setPadding (and the deprecated widgetSetEdgeInsets it aliases) is implemented for only four AppKit class families. It silently no-ops on every other widget, including Button and text labels, which the design system needs to pad. A caller has no way to tell that the call did nothing.
Environment
- Perry 0.5.1531 (local build)
- macOS, Darwin 25.6.0, arm64
- Native macOS target (
perry-ui-macos)
Cause (confirmed in source)
setPadding and widgetSetEdgeInsets compile to the same FFI, perry_ui_widget_set_edge_insets (crates/perry-codegen/src/lower_call/ui_styling.rs), which calls set_edge_insets in crates/perry-ui-macos/src/widgets/padding.rs. That function branches on four classes only — NSStackView, NSTextField (and only when the widget installed the Perry inset cell), NSTextView, NSScrollView — and then ends. There is no catch-all, so any other widget class falls through and the call is a no-op. Labels install no inset cell (only textfield.rs and securefield.rs do), so an NSTextField label also no-ops.
What works vs what no-ops
Padding works:
| Handled class |
Widgets |
NSStackView |
hstack, vstack, zstack, navstack, bottom_nav, toggle |
NSTextField + inset cell |
textfield, securefield |
NSTextView |
textarea, rich_text |
NSScrollView |
scrollview, image_gallery |
Padding no-ops:
| Unhandled class |
Widgets |
NSButton |
button |
NSTextField label (no inset cell) |
text, toast, attributed_text, text_registry |
NSImageView |
image, qrcode |
NSDatePicker |
date_picker, calendar |
NSComboBox |
combobox |
NSPopUpButton |
picker |
NSSlider |
slider |
NSProgressIndicator |
progressview |
NSBox |
divider, form |
NSTableView |
table, lazyvstack, command_palette |
NSOutlineView |
tree_view |
plain NSView |
adbanner, spacer |
NSWindow |
sheet |
Impact
Button and text labels are the clear cases: the design system sizes a medium button by its horizontal padding (0 var(--space-5)), and pads label chips. With the no-op, a button cannot be padded, so its width has to be hard-coded. Other controls (slider, progress, spacer) may not need padding, but the silent no-op gives a caller no signal either way.
For comparison, iOS applies button padding — UIButton via configuration.setContentInsets (fallback setContentEdgeInsets) in perry-ui-ios/src/widgets/padding.rs — so this is a macOS-specific gap, not a cross-platform limitation.
Suggested fix
Give the common leaf widgets a real padding path on macOS, starting with the two the design system needs: NSButton (content insets via NSButton.Configuration.contentInsets, or a content wrapper) and NSTextField labels. Where a widget genuinely cannot be padded, consider a debug-time warning rather than a silent no-op so a caller learns the call had no effect.
Related
Scope
macOS target. Every widget whose backing class is not one of the four handled families.
Summary
On macOS,
setPadding(and the deprecatedwidgetSetEdgeInsetsit aliases) is implemented for only four AppKit class families. It silently no-ops on every other widget, includingButtonand text labels, which the design system needs to pad. A caller has no way to tell that the call did nothing.Environment
perry-ui-macos)Cause (confirmed in source)
setPaddingandwidgetSetEdgeInsetscompile to the same FFI,perry_ui_widget_set_edge_insets(crates/perry-codegen/src/lower_call/ui_styling.rs), which callsset_edge_insetsincrates/perry-ui-macos/src/widgets/padding.rs. That function branches on four classes only —NSStackView,NSTextField(and only when the widget installed the Perry inset cell),NSTextView,NSScrollView— and then ends. There is no catch-all, so any other widget class falls through and the call is a no-op. Labels install no inset cell (onlytextfield.rsandsecurefield.rsdo), so anNSTextFieldlabel also no-ops.What works vs what no-ops
Padding works:
NSStackViewNSTextField+ inset cellNSTextViewNSScrollViewPadding no-ops:
NSButtonNSTextFieldlabel (no inset cell)NSImageViewNSDatePickerNSComboBoxNSPopUpButtonNSSliderNSProgressIndicatorNSBoxNSTableViewNSOutlineViewNSViewNSWindowImpact
Buttonand text labels are the clear cases: the design system sizes a medium button by its horizontal padding (0 var(--space-5)), and pads label chips. With the no-op, a button cannot be padded, so its width has to be hard-coded. Other controls (slider, progress, spacer) may not need padding, but the silent no-op gives a caller no signal either way.For comparison, iOS applies button padding —
UIButtonviaconfiguration.setContentInsets(fallbacksetContentEdgeInsets) inperry-ui-ios/src/widgets/padding.rs— so this is a macOS-specific gap, not a cross-platform limitation.Suggested fix
Give the common leaf widgets a real padding path on macOS, starting with the two the design system needs:
NSButton(content insets viaNSButton.Configuration.contentInsets, or a content wrapper) andNSTextFieldlabels. Where a widget genuinely cannot be padded, consider a debug-time warning rather than a silent no-op so a caller learns the call had no effect.Related
TextField,TextArea, andSecureField. This issue is the unfinished tail of the same problem:Button, text labels, and the other leaf widgets listed above still no-op.Scope
macOS target. Every widget whose backing class is not one of the four handled families.