Upgrade MyGo to v0.5.5
Elements are now pass-scoped value handles: tests that read bounds after a frame bind a ui.Handle. pixel tells frames apart by the build pass's root, since the Tester clock no longer moves on its own. Fix the regressions the upgrade brought: - Clipboard reads in input handlers go through c.Services(). - MyGo notes an overlay's opener after building it, so AutoFocus inside lost it; pixel.AutoFocus focuses through a Handle, and archtest bans the raw call. Dialogs hand the focus back again. - Elements around a press stay hovered: Tooltip tells a press-given focus by focus arriving under the pointer. - The web host moves the Tester clock with real time, at most 500 ms a call, and gives components the real clock. The gallery's source index also accepts CRLF checkouts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1f3d38110e
commit
03e1f91706
532 files changed
+828
-735
No files matched your search
@@ -21,7 +21,7 @@ type AttachmentChipOptions struct {
|
||||
}
|
||||
|
||||
type AttachmentChipResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Removed bool
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ type CallControlsOptions struct {
|
||||
}
|
||||
|
||||
type CallControlsResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Changed, HungUp bool
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ func CallControls(c *ui.Context, text string, muted, camera *bool, o CallControl
|
||||
k := pixel.K(c)
|
||||
var res CallControlsResult
|
||||
// A slash crosses a sprite that is off.
|
||||
button := func(base func() *ui.Element, sp *pixel.Sprite, fill, ink pixel.Slot, off bool) bool {
|
||||
shell, b, st := pixel.Shell(c, func() *ui.Element { return base().Disabled(o.Disabled).FocusRing(false) })
|
||||
button := func(base func() ui.Element, sp *pixel.Sprite, fill, ink pixel.Slot, off bool) bool {
|
||||
shell, b, st := pixel.Shell(c, func() ui.Element { return base().Disabled(o.Disabled).FocusRing(false) })
|
||||
shell.Size(k.D(22), k.D(22)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
g.Frame(0, 0, 22, 22, pixel.Raised, fill)
|
||||
@@ -39,15 +39,15 @@ func CallControls(c *ui.Context, text string, muted, camera *bool, o CallControl
|
||||
}
|
||||
m, cam := *muted, *camera
|
||||
res.Element = ui.Row(c).Gap(k.D(4)).FocusGroup(ui.Horizontal).Role(ui.RoleToolbar).Label(text).Children(func() {
|
||||
if button(func() *ui.Element { return ui.ToggleBase(c, &m).Label(k.Msg("mute")) }, icon.Mic, pixel.Face, pixel.Ink, *muted) {
|
||||
if button(func() ui.Element { return ui.ToggleBase(c, &m).Label(k.Msg("mute")) }, icon.Mic, pixel.Face, pixel.Ink, *muted) {
|
||||
*muted, res.Changed = m, true
|
||||
slog.Debug("8bit: call mute", "label", text, "muted", *muted)
|
||||
}
|
||||
if button(func() *ui.Element { return ui.ToggleBase(c, &cam).Label(k.Msg("camera")) }, icon.Camera, pixel.Face, pixel.Ink, !*camera) {
|
||||
if button(func() ui.Element { return ui.ToggleBase(c, &cam).Label(k.Msg("camera")) }, icon.Camera, pixel.Face, pixel.Ink, !*camera) {
|
||||
*camera, res.Changed = cam, true
|
||||
slog.Debug("8bit: call camera", "label", text, "on", *camera)
|
||||
}
|
||||
if button(func() *ui.Element { return ui.ButtonBase(c).Label(k.Msg("hang_up")) }, icon.Phone, pixel.Bad, pixel.AccentInk, false) {
|
||||
if button(func() ui.Element { return ui.ButtonBase(c).Label(k.Msg("hang_up")) }, icon.Phone, pixel.Bad, pixel.AccentInk, false) {
|
||||
res.HungUp = true
|
||||
slog.Debug("8bit: call hung up", "label", text)
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ type Channel struct {
|
||||
}
|
||||
|
||||
type ChannelListResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Submitted is Enter or a double click on a row.
|
||||
Changed, Submitted bool
|
||||
}
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ type Message struct {
|
||||
|
||||
// listed is a chat list's choice; Submitted is Enter.
|
||||
type listed struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Changed, Submitted bool
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func list(c *ui.Context, k pixel.Kit, text string, n int, selected *int, w, h in
|
||||
}
|
||||
|
||||
// para wraps s to w dots, hugging its lines.
|
||||
func para(c *ui.Context, k pixel.Kit, s string, w, maxLines int, ink pixel.Slot) *ui.Element {
|
||||
func para(c *ui.Context, k pixel.Kit, s string, w, maxLines int, ink pixel.Slot) ui.Element {
|
||||
lines := pixel.Wrap(s, w, maxLines)
|
||||
lw := 0
|
||||
for _, l := range lines {
|
||||
@@ -75,7 +75,7 @@ func tagW(s string) int {
|
||||
}
|
||||
|
||||
// rule centers label on a line across w dots.
|
||||
func rule(c *ui.Context, k pixel.Kit, label string, w int, ink, edge pixel.Slot) *ui.Element {
|
||||
func rule(c *ui.Context, k pixel.Kit, label string, w int, ink, edge pixel.Slot) ui.Element {
|
||||
tw := tagW(label)
|
||||
if w < tw+16 {
|
||||
panic(fmt.Sprintf("chat: a %d-dot rule cannot hold %q", w, label))
|
||||
|
||||
+1
-1
@@ -262,7 +262,7 @@ func TestDateSeparatorNamesTheDay(t *testing.T) {
|
||||
day := wd.clk.t
|
||||
pixel.ResetDue()
|
||||
tt := view(400, 100, wd, func(c *ui.Context) { chat.DateSeparator(c, day, chat.DateSeparatorOptions{}) })
|
||||
if until := time.Until(pixel.Due()); until <= 0 || until > 2*time.Second {
|
||||
if until := pixel.Due().Sub(tt.Now()); until <= 0 || until > 2*time.Second {
|
||||
t.Fatalf("the next build is due in %v, not at midnight", until)
|
||||
}
|
||||
wd.clk.t = wd.clk.t.Add(2 * time.Second)
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ type Conversation struct {
|
||||
}
|
||||
|
||||
type ChatListResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Submitted is Enter or a double click on a row.
|
||||
Changed, Submitted bool
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ type ChatLogOptions struct {
|
||||
type logKey struct{}
|
||||
|
||||
// ChatLog scrolls messages and follows the end while there.
|
||||
func ChatLog(c *ui.Context, text string, o ChatLogOptions, content func()) *ui.Element {
|
||||
func ChatLog(c *ui.Context, text string, o ChatLogOptions, content func()) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w, h := pixel.Or("ChatLog Width", o.Width, 220), pixel.Or("ChatLog Height", o.Height, 140)
|
||||
box := ui.Box(c).Size(k.D(w), k.D(h))
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ type ComposerOptions struct {
|
||||
}
|
||||
|
||||
type ComposerResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Changed bool
|
||||
// Sent is the text sent this frame, else empty.
|
||||
Sent string
|
||||
|
||||
@@ -16,7 +16,7 @@ type DateSeparatorOptions struct {
|
||||
}
|
||||
|
||||
// DateSeparator rules a line with TODAY, YESTERDAY or the date.
|
||||
func DateSeparator(c *ui.Context, day time.Time, o DateSeparatorOptions) *ui.Element {
|
||||
func DateSeparator(c *ui.Context, day time.Time, o DateSeparatorOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
now := k.Now()
|
||||
// At midnight today becomes yesterday.
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ type EmojiPickerOptions struct {
|
||||
}
|
||||
|
||||
type EmojiPickerResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Picked is the emoji chosen this frame, else empty.
|
||||
Picked string
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ type LetterOptions struct {
|
||||
}
|
||||
|
||||
// Letter sets a body on parchment with a signature.
|
||||
func Letter(c *ui.Context, body string, o LetterOptions) *ui.Element {
|
||||
func Letter(c *ui.Context, body string, o LetterOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w := pixel.Or("Letter Width", o.Width, 180)
|
||||
if w < 40 {
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ type LinkPreviewOptions struct {
|
||||
}
|
||||
|
||||
type LinkPreviewResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Clicked bool
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func LinkPreview(c *ui.Context, url, title, site string, o LinkPreviewOptions) L
|
||||
// The site sits under the title; the card fits both.
|
||||
sy := 4 + len(lines)*pixel.LineHeight
|
||||
h := max(40, sy+12)
|
||||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||||
shell, base, st := pixel.Shell(c, func() ui.Element {
|
||||
return ui.Box(c).Focusable().Role(ui.RoleLink).Label(title).Description(url).Disabled(o.Disabled).Cursor(ui.CursorPointer).FocusRing(false)
|
||||
})
|
||||
shell.Size(k.D(w), k.D(h)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ type MailboxOptions struct {
|
||||
}
|
||||
|
||||
type MailboxResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Changed bool
|
||||
// Opened is the mail opened this frame, else -1.
|
||||
Opened int
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ type MailReaderOptions struct {
|
||||
}
|
||||
|
||||
// MailReader shows a mail's headers, a rule, its body.
|
||||
func MailReader(c *ui.Context, m Mail, o MailReaderOptions) *ui.Element {
|
||||
func MailReader(c *ui.Context, m Mail, o MailReaderOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w := pixel.Or("MailReader Width", o.Width, 220)
|
||||
return ui.Column(c).Width(k.D(w)).Gap(k.D(4)).Children(func() {
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ type MemberListOptions struct {
|
||||
}
|
||||
|
||||
// MemberList groups members online and offline, with status dots.
|
||||
func MemberList(c *ui.Context, text string, members []Member, o MemberListOptions) *ui.Element {
|
||||
func MemberList(c *ui.Context, text string, members []Member, o MemberListOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w := pixel.Or("MemberList Width", o.Width, 140)
|
||||
var on, off []Member
|
||||
|
||||
@@ -19,7 +19,7 @@ type MentionInputOptions struct {
|
||||
}
|
||||
|
||||
type MentionInputResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Changed, Submitted, Accepted bool
|
||||
// Mentioned is the member just completed, else empty.
|
||||
Mentioned string
|
||||
|
||||
@@ -16,7 +16,7 @@ type NotificationBellOptions struct {
|
||||
}
|
||||
|
||||
type NotificationBellResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Clicked bool
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func NotificationBell(c *ui.Context, text string, n int, o NotificationBellOptio
|
||||
if n < 0 {
|
||||
panic(fmt.Sprintf("chat: NotificationBell count %d", n))
|
||||
}
|
||||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||||
shell, base, st := pixel.Shell(c, func() ui.Element {
|
||||
return ui.ButtonBase(c).Label(text).Description(fmt.Sprintf(k.Msg("new_count"), n)).Disabled(o.Disabled).FocusRing(false)
|
||||
})
|
||||
s := ui.Local(shell, bellKey{}, func() bellState { return bellState{last: n} })
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ type PollOptions struct {
|
||||
}
|
||||
|
||||
type PollResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Voted is the option voted for this frame, else -1.
|
||||
Voted int
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func Poll(c *ui.Context, question string, opts []PollOption, mine *int, o PollOp
|
||||
// bar fills an option's share; ▶ marks your vote.
|
||||
func bar(c *ui.Context, k pixel.Kit, op PollOption, total int, mine bool, w int, disabled bool) bool {
|
||||
label := fmt.Sprintf("%s, %d", op.Text, op.Votes)
|
||||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||||
shell, base, st := pixel.Shell(c, func() ui.Element {
|
||||
return ui.ButtonBase(c).Label(label).Disabled(disabled).FocusRing(false)
|
||||
})
|
||||
n := strconv.Itoa(op.Votes)
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ type ReactionsOptions struct {
|
||||
}
|
||||
|
||||
type ReactionsResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Toggled is the chip clicked this frame, else -1.
|
||||
Toggled int
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func Reactions(c *ui.Context, text string, rs []Reaction, o ReactionsOptions) Re
|
||||
art := emoji(r.Emoji)
|
||||
// The base turns before it learns it is off.
|
||||
v := r.Mine
|
||||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||||
shell, base, st := pixel.Shell(c, func() ui.Element {
|
||||
return ui.ToggleBase(c, &v).Label(fmt.Sprintf("%s %d", r.Emoji, r.Count)).Disabled(o.Disabled).FocusRing(false)
|
||||
})
|
||||
if v != r.Mine && !base.IsDisabled() {
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ var check = pixel.MustSprite(`
|
||||
`)
|
||||
|
||||
// ReadReceipt shows one check once sent, two once read.
|
||||
func ReadReceipt(c *ui.Context, read bool, o ReadReceiptOptions) *ui.Element {
|
||||
func ReadReceipt(c *ui.Context, read bool, o ReadReceiptOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
sc := pixel.Or("ReadReceipt Scale", o.Scale, 1)
|
||||
if sc > 4 {
|
||||
|
||||
@@ -14,7 +14,7 @@ type SpeechBubbleOptions struct {
|
||||
}
|
||||
|
||||
// SpeechBubble shows who said what, when; tail their side.
|
||||
func SpeechBubble(c *ui.Context, m Message, o SpeechBubbleOptions) *ui.Element {
|
||||
func SpeechBubble(c *ui.Context, m Message, o SpeechBubbleOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w := pixel.Or("SpeechBubble Width", o.Width, 140)
|
||||
// Yours sit right on Accent, written in AccentInk.
|
||||
|
||||
@@ -14,7 +14,7 @@ type StickerPickerOptions struct {
|
||||
}
|
||||
|
||||
type StickerPickerResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Picked is the sticker chosen this frame, else empty.
|
||||
Picked string
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ var diamond = pixel.MustSprite(`
|
||||
`)
|
||||
|
||||
// SystemMessage centers a notice, such as PLAYER 2 HAS JOINED.
|
||||
func SystemMessage(c *ui.Context, text string, o SystemMessageOptions) *ui.Element {
|
||||
func SystemMessage(c *ui.Context, text string, o SystemMessageOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
ink := o.Slot
|
||||
if ink == pixel.Ink {
|
||||
|
||||
@@ -15,7 +15,7 @@ type TypingIndicatorOptions struct {
|
||||
}
|
||||
|
||||
// TypingIndicator bounces three dots in a bubble while name types.
|
||||
func TypingIndicator(c *ui.Context, name string, o TypingIndicatorOptions) *ui.Element {
|
||||
func TypingIndicator(c *ui.Context, name string, o TypingIndicatorOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
ink := o.Slot
|
||||
if ink == pixel.Ink {
|
||||
|
||||
@@ -12,7 +12,7 @@ type UnreadDividerOptions struct {
|
||||
}
|
||||
|
||||
// UnreadDivider rules NEW MESSAGES in Bad above the first unread.
|
||||
func UnreadDivider(c *ui.Context, o UnreadDividerOptions) *ui.Element {
|
||||
func UnreadDivider(c *ui.Context, o UnreadDividerOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
return rule(c, k, k.Msg("new_messages"), pixel.Or("UnreadDivider Width", o.Width, 200), pixel.Bad, pixel.Bad)
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ type UserCardOptions struct {
|
||||
}
|
||||
|
||||
type UserCardResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Clicked is the action clicked this frame, else -1.
|
||||
Clicked int
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ type VoiceMeterOptions struct {
|
||||
}
|
||||
|
||||
// VoiceMeter lights bars for 0–1: green, amber, then red.
|
||||
func VoiceMeter(c *ui.Context, text string, level float64, o VoiceMeterOptions) *ui.Element {
|
||||
func VoiceMeter(c *ui.Context, text string, level float64, o VoiceMeterOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
if math.IsNaN(level) || level < 0 || level > 1 {
|
||||
panic(fmt.Sprintf("chat: VoiceMeter level %v outside 0–1", level))
|
||||
|
||||
Reference in new issue
Block a user