Skip to content

macOS: TextField text sits 2pt higher than NSTextField when not editing, and jumps down on focus #11661

Description

@steinybot

Summary

On macOS, a TextField that is not being edited draws its text 2pt higher than a stock AppKit NSTextField. When the field gains focus, AppKit's field editor draws the text at the stock position, so the text jumps down 2pt. A Text label is not affected.

Measured

Borderless fields, 40pt tall, 16pt Helvetica. Distance from the field's top edge to the top of the "H":

Not editing Editing
Stock AppKit NSTextField 3.5pt 3.5pt
Perry TextField 1.5pt 3.5pt

A Perry Text label and a stock NSTextField(labelWithString:) both put the "H" 3.5pt below the frame's top, so labels match.

The offset shows with zero insets, so it does not come from setPadding. Perry replaces each field's cell with PerryInsetTextFieldCell (install_text_field_cell in crates/perry-ui-macos/src/widgets/padding.rs), and the difference is in how that cell draws text when the field is not editing.

Repro

import { App, TextField, VStack, textSetFontFamily, textfieldFocus, textfieldSetBackgroundColor, textfieldSetBorderless, textfieldSetFontSize, textfieldSetString, textfieldSetTextColor, widgetSetBackgroundColor, widgetSetHeight } from "perry/ui"

function field(text: string) {
  const f = TextField("", () => {})
  textfieldSetBorderless(f, 1)
  textfieldSetFontSize(f, 16)
  textSetFontFamily(f, "Helvetica")
  textfieldSetBackgroundColor(f, 1, 1, 0.7, 1)
  textfieldSetTextColor(f, 0, 0, 0, 1)
  widgetSetHeight(f, 40)
  textfieldSetString(f, text)
  return f
}
const first = field("Hxg first")
const second = field("Hxg second")
const body = VStack(40, [first, second])
widgetSetBackgroundColor(body, 1, 1, 1, 1)
App({ title: "probe", width: 400, height: 200, windowState: "normal", body })

The first field opens as the first responder, so it draws through the field editor. Its text sits 2pt lower in its box than the second field's text. Click into the second field and its text drops 2pt.

For the stock comparison, the same two fields built in Swift (NSTextField(frame:), isBezeled = false, isBordered = false, 16pt Helvetica, 40pt tall) draw the "H" 3.5pt below the top whether or not the field is editing.

Expected

A TextField draws its text in the same place whether or not it is editing, matching a stock NSTextField.

Perry: main at 4a1b788 with the sRGB change from #11613 applied, which touches only colour creation. macOS 26.6.2.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions