Trim needless comments and fix the picker width
Remove comments that only restate the code, keeping the section banners and the notes that carry a reason, a measured fact or an external contract. ButtonOptions.width claimed to be a fixed width but only set minWidth, so the Graph and Texture pickers grew with the name they showed. It is now what it promises, with a separate minWidth for the buttons that have two forms. Narrow buttons shrink their padding, otherwise a 24px icon button had 8px left for its glyph and truncate dropped it entirely. __gm.labelsDropped() guards that class: the overflow guard only saw text that was too wide, never a label truncated to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f16801aef3
commit
4d84231b9f
115 files changed
+707
-1564
No files matched your search
+23
-23
@@ -6,29 +6,26 @@ import { type Size, Widget } from '../Widget';
|
||||
import { truncate } from './Label';
|
||||
|
||||
export interface ButtonOptions {
|
||||
/** Visual weight: 'primary' uses the accent color, 'ghost' has no background until hovered. */
|
||||
variant?: 'default' | 'primary' | 'ghost' | 'danger';
|
||||
/** Fixed width; otherwise measured from the label. */
|
||||
/** Fixed width; otherwise measured from the label, which is truncated when too long. */
|
||||
width?: number;
|
||||
/**
|
||||
* Label position in a button wider than its text. 'center' suits standalone buttons,
|
||||
* 'left' the ones that read as a list or a field (menu entries, dropdowns).
|
||||
*/
|
||||
/** Floor only – for a button whose label changes between forms and must grow with them. */
|
||||
minWidth?: number;
|
||||
align?: 'center' | 'left';
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/** Push button; Enter/Space activate it while focused. Clicks fire only for the primary pointer button. */
|
||||
export class Button extends Widget {
|
||||
private readonly bg = new Graphics();
|
||||
private readonly labelText: Text;
|
||||
/** The label as set. `labelText` holds the possibly shortened version that is on screen. */
|
||||
/** The label as set; `labelText` holds the possibly truncated version on screen. */
|
||||
private fullText: string;
|
||||
private pressed = false;
|
||||
/** Caller-owned toggle state: renders like a primary button but is not flipped by clicks. */
|
||||
/** Caller-owned: renders like a primary button but is never flipped by a click. */
|
||||
toggled = false;
|
||||
variant: NonNullable<ButtonOptions['variant']>;
|
||||
protected readonly align: NonNullable<ButtonOptions['align']>;
|
||||
protected readonly fixedWidth: number;
|
||||
onClick?: () => void;
|
||||
|
||||
constructor(text: string, options: ButtonOptions = {}) {
|
||||
@@ -39,7 +36,8 @@ export class Button extends Widget {
|
||||
this.focusable = true;
|
||||
this.cursor = 'pointer';
|
||||
this.minHeight = Theme.controlHeight;
|
||||
if (options.width) this.minWidth = options.width;
|
||||
this.fixedWidth = options.width ?? 0;
|
||||
this.minWidth = this.fixedWidth || (options.minWidth ?? 0);
|
||||
this.fullText = text;
|
||||
this.labelText = new Text({ text, style: textStyle(this.variant === 'primary' ? 'accent' : 'body') });
|
||||
this.addChild(this.bg, this.labelText);
|
||||
@@ -65,11 +63,15 @@ export class Button extends Widget {
|
||||
});
|
||||
}
|
||||
|
||||
/** The full label, not the truncated version on screen. */
|
||||
get text(): string {
|
||||
return this.fullText;
|
||||
}
|
||||
|
||||
/** What is drawn – shorter than `text` when the button is too narrow to hold it. */
|
||||
get shownText(): string {
|
||||
return this.labelText.text;
|
||||
}
|
||||
|
||||
/** Repaints only; the parent must relayout when the measured width matters. */
|
||||
setText(text: string): void {
|
||||
if (text === this.fullText) return;
|
||||
@@ -83,27 +85,25 @@ export class Button extends Widget {
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
/** Whether the label sits on a filled accent surface and therefore needs the light style. */
|
||||
protected get onAccent(): boolean {
|
||||
return this.toggled || this.variant === 'primary' || this.variant === 'danger';
|
||||
}
|
||||
|
||||
override measure(): Size {
|
||||
// Measured from the full label, never from what is currently on screen: measuring the
|
||||
// truncated text would let a button that was once too narrow stay too narrow for good.
|
||||
const h = Math.max(this.minHeight, Theme.controlHeight);
|
||||
// A given width is a width, not a floor: a picker must not resize around the name it shows.
|
||||
if (this.fixedWidth) return { w: this.fixedWidth, h };
|
||||
// From the full label, never the on-screen one: measuring truncated text would keep a button
|
||||
// that was once too narrow too narrow for good.
|
||||
const metrics = CanvasTextMetrics.measureText(this.fullText, this.labelText.style);
|
||||
return {
|
||||
w: Math.max(this.minWidth, Math.ceil(metrics.width) + Theme.padding * 2),
|
||||
h: Math.max(this.minHeight, Theme.controlHeight),
|
||||
};
|
||||
return { w: Math.max(this.minWidth, Math.ceil(metrics.width) + Theme.padding * 2), h };
|
||||
}
|
||||
|
||||
/**
|
||||
* Width the label may occupy. Subclasses that draw something else inside the button take
|
||||
* their room off here, so the text is shortened instead of running underneath it.
|
||||
*/
|
||||
/** Width the label may occupy; subclasses that draw inside the button take their room off here. */
|
||||
protected labelSlot(): number {
|
||||
return this.w - Theme.padding * 2;
|
||||
// The padding is an inset, not a claim: on a 24 px icon button it would leave 8 px and
|
||||
// truncate the glyph away entirely. From 48 px up this is the full padding again.
|
||||
return this.w - Math.min(Theme.padding, Math.floor(this.w / 6)) * 2;
|
||||
}
|
||||
|
||||
override onKeyDown(event: KeyboardEvent): boolean {
|
||||
|
||||
Reference in new issue
Block a user