From 03e1f917066fdb806f38b87428ea3378966506f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 17 Oct 2026 01:31:08 -0400 Subject: [PATCH] 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) --- AGENTS.md | 13 ++++----- README.md | 2 +- THIRD_PARTY_NOTICES.md | 2 +- account/about.go | 2 +- account/accessibilityoptions.go | 2 +- account/account.go | 4 +-- account/accountswitcher.go | 4 +-- account/charactercreator.go | 2 +- account/controllermap.go | 2 +- account/difficultyselect.go | 2 +- account/feedbackform.go | 2 +- account/keybindings.go | 4 +-- account/loginform.go | 2 +- account/optioncycler.go | 4 +-- account/palettepicker.go | 2 +- account/passwordgrid.go | 2 +- account/patchnotes.go | 2 +- account/playerselect.go | 2 +- account/profilecard.go | 2 +- account/sessionlist.go | 2 +- account/settingslist.go | 2 +- account/twofactor.go | 2 +- account/volumemixer.go | 2 +- agent/agentcard.go | 2 +- agent/agentlog.go | 2 +- agent/agentstatus.go | 2 +- agent/approvalprompt.go | 4 +-- agent/checkpoints.go | 2 +- agent/costcoins.go | 2 +- agent/diffscroll.go | 2 +- agent/memorycrystals.go | 2 +- agent/modelselect.go | 2 +- agent/planquest.go | 2 +- agent/promptbox.go | 2 +- agent/subagentparty.go | 2 +- agent/thinkingorb.go | 2 +- agent/tokenmana.go | 2 +- agent/toolcall.go | 2 +- chart/areachart.go | 2 +- chart/axis.go | 2 +- chart/barchart.go | 2 +- chart/boxplot.go | 2 +- chart/bubblechart.go | 2 +- chart/bulletchart.go | 2 +- chart/calendarheatmap.go | 2 +- chart/candlestick.go | 2 +- chart/crosshair.go | 2 +- chart/donutchart.go | 2 +- chart/funnel.go | 2 +- chart/gauge.go | 2 +- chart/heatmap.go | 2 +- chart/histogram.go | 2 +- chart/horizontalbar.go | 2 +- chart/legend.go | 4 +-- chart/linechart.go | 2 +- chart/piechart.go | 2 +- chart/polararea.go | 2 +- chart/progressring.go | 2 +- chart/pyramid.go | 2 +- chart/radarchart.go | 2 +- chart/scatterplot.go | 2 +- chart/sparkline.go | 2 +- chart/spectrumbars.go | 2 +- chart/stackedbar.go | 2 +- chart/stepchart.go | 2 +- chart/treemap.go | 2 +- chart/waterfall.go | 2 +- chart/winloss.go | 2 +- chat/attachmentchip.go | 2 +- chat/callcontrols.go | 12 ++++----- chat/channellist.go | 2 +- chat/chat.go | 6 ++--- chat/chat_test.go | 2 +- chat/chatlist.go | 2 +- chat/chatlog.go | 2 +- chat/composer.go | 2 +- chat/dateseparator.go | 2 +- chat/emojipicker.go | 2 +- chat/letter.go | 2 +- chat/linkpreview.go | 4 +-- chat/mailbox.go | 2 +- chat/mailreader.go | 2 +- chat/memberlist.go | 2 +- chat/mentioninput.go | 2 +- chat/notificationbell.go | 4 +-- chat/poll.go | 4 +-- chat/reactions.go | 4 +-- chat/readreceipt.go | 2 +- chat/speechbubble.go | 2 +- chat/stickerpicker.go | 2 +- chat/systemmessage.go | 2 +- chat/typingindicator.go | 2 +- chat/unreaddivider.go | 2 +- chat/usercard.go | 2 +- chat/voicemeter.go | 2 +- cmd/web/main.go | 22 ++++++++++++++-- code/breakpoints.go | 2 +- code/codeblock.go | 2 +- code/commandhistory.go | 2 +- code/crashscreen.go | 2 +- code/debuggrid.go | 2 +- code/diffview.go | 2 +- code/fpsmeter.go | 2 +- code/glyphmap.go | 2 +- code/guards_test.go | 4 +-- code/hexviewer.go | 2 +- code/logviewer.go | 2 +- code/memorymap.go | 2 +- code/paletteeditor.go | 2 +- code/pixeleditor.go | 4 +-- code/profilerbars.go | 2 +- code/registerview.go | 2 +- code/shortcutlist.go | 2 +- code/spritesheet.go | 2 +- code/terminal.go | 2 +- data/accordion.go | 2 +- data/bestiary.go | 2 +- data/cardgrid.go | 2 +- data/carousel.go | 2 +- data/collapsible.go | 4 +-- data/data.go | 2 +- data/descriptionlist.go | 2 +- data/feed.go | 2 +- data/filterbar.go | 2 +- data/gridview.go | 2 +- data/infinitelist.go | 4 +-- data/inventory.go | 2 +- data/itemslot.go | 2 +- data/leaderboard.go | 2 +- data/list.go | 4 +-- data/matrix.go | 2 +- data/outline.go | 4 +-- data/pager.go | 2 +- data/propertygrid.go | 2 +- data/sortable.go | 4 +-- data/statcompare.go | 2 +- data/table.go | 6 ++--- data/timeline.go | 2 +- data/transfer.go | 2 +- data/tree.go | 4 +-- datetime/agenda.go | 2 +- datetime/alarm.go | 2 +- datetime/analogclock.go | 2 +- datetime/calendar.go | 2 +- datetime/clocks_test.go | 2 +- datetime/countdown.go | 2 +- datetime/dateinput.go | 6 ++--- datetime/daterange.go | 2 +- datetime/dayschedule.go | 2 +- datetime/digitalclock.go | 2 +- datetime/eventchip.go | 2 +- datetime/grid.go | 6 ++--- datetime/monthpicker.go | 2 +- datetime/relativetime.go | 2 +- datetime/seasonwheel.go | 4 +-- datetime/stopwatch.go | 2 +- datetime/sundial.go | 4 +-- datetime/timeinput.go | 4 +-- datetime/timer.go | 2 +- datetime/views_test.go | 4 +-- datetime/weekstrip.go | 4 +-- datetime/yearpicker.go | 2 +- datetime/yearview.go | 2 +- desk/appwindow.go | 4 +-- desk/bootscreen.go | 2 +- desk/calculator.go | 2 +- desk/clipboardpanel.go | 4 +-- desk/controlpanel.go | 2 +- desk/desktop.go | 2 +- desk/desktopicon.go | 4 +-- desk/diskdefrag.go | 2 +- desk/dock.go | 4 +-- desk/lockscreen.go | 4 +-- desk/minesweeper.go | 2 +- desk/notepad.go | 2 +- desk/rundialog.go | 2 +- desk/screensaver.go | 2 +- desk/shutdownscreen.go | 2 +- desk/snake.go | 4 +-- desk/startmenu.go | 6 ++--- desk/taskbar.go | 2 +- desk/trayicons.go | 4 +-- desk/veil.go | 4 +-- desk/wallpaperpicker.go | 2 +- display/avatar.go | 2 +- display/avatargroup.go | 2 +- display/badge.go | 2 +- display/bubble.go | 2 +- display/display.go | 2 +- display/emoji.go | 2 +- display/figure.go | 2 +- display/flag.go | 2 +- display/gem.go | 2 +- display/icon.go | 2 +- display/keyvalue.go | 2 +- display/medal.go | 2 +- display/picture.go | 2 +- display/placeholder.go | 2 +- display/portrait.go | 2 +- display/qrcode.go | 2 +- display/rarityframe.go | 2 +- display/ribbon.go | 2 +- display/signpost.go | 4 +-- display/sprite.go | 2 +- display/stamp.go | 2 +- display/stat.go | 2 +- display/statusdot.go | 2 +- display/swatch.go | 2 +- display/tag.go | 4 +-- display/trophy.go | 2 +- feedback/achievement.go | 2 +- feedback/alert.go | 2 +- feedback/banner.go | 2 +- feedback/barsloader.go | 2 +- feedback/busyoverlay.go | 4 +-- feedback/combocounter.go | 2 +- feedback/continueprompt.go | 2 +- feedback/countup.go | 2 +- feedback/dotsloader.go | 2 +- feedback/emptystate.go | 2 +- feedback/errorstate.go | 2 +- feedback/hourglass.go | 2 +- feedback/inlinemessage.go | 2 +- feedback/levelup.go | 2 +- feedback/loadingscreen.go | 2 +- feedback/meter.go | 2 +- feedback/progress.go | 2 +- feedback/pulse.go | 2 +- feedback/readygo.go | 2 +- feedback/saveindicator.go | 2 +- feedback/shake.go | 2 +- feedback/skeleton.go | 2 +- feedback/spinner.go | 2 +- feedback/successstate.go | 2 +- feedback/toasts.go | 2 +- files/archiveviewer.go | 2 +- files/diskusage.go | 2 +- files/filedetails.go | 2 +- files/fileexplorer.go | 2 +- files/filegrid.go | 2 +- files/fileicon.go | 2 +- files/filepreview.go | 2 +- files/memorycard.go | 2 +- files/pathbar.go | 4 +-- files/quotameter.go | 2 +- files/recentfiles.go | 2 +- files/sharepanel.go | 2 +- files/syncstatus.go | 2 +- files/trash.go | 2 +- files/uploadqueue.go | 2 +- fx/aurora.go | 2 +- fx/clouds.go | 2 +- fx/confetti.go | 2 +- fx/dissolve.go | 2 +- fx/explosion.go | 2 +- fx/fadedither.go | 2 +- fx/fire.go | 2 +- fx/fireflies.go | 2 +- fx/flash.go | 2 +- fx/fx.go | 4 +-- fx/glitch.go | 2 +- fx/iriswipe.go | 2 +- fx/lava.go | 2 +- fx/leaves.go | 2 +- fx/lightning.go | 2 +- fx/mosaic.go | 2 +- fx/mountains.go | 2 +- fx/ocean.go | 2 +- fx/palettecycle.go | 2 +- fx/petals.go | 2 +- fx/rain.go | 2 +- fx/scanlines.go | 2 +- fx/screenshake.go | 2 +- fx/seasonscene.go | 4 +-- fx/sky.go | 2 +- fx/snow.go | 2 +- fx/sparkle.go | 2 +- fx/starfield.go | 2 +- fx/sunmoon.go | 2 +- fx/vignette.go | 2 +- fx/waterfall.go | 2 +- gallery/app.go | 7 ++++- gallery/boot.go | 4 +-- gallery/chrome.go | 2 +- gallery/demos_foundations.go | 4 +-- gallery/demos_popups.go | 4 +-- gallery/registry.go | 4 ++- game/achievements.go | 2 +- game/atbgauge.go | 2 +- game/battlelog.go | 2 +- game/bossbar.go | 2 +- game/charactersheet.go | 2 +- game/coincounter.go | 2 +- game/compass.go | 2 +- game/crafting.go | 2 +- game/credits.go | 2 +- game/damagenumbers.go | 2 +- game/endingcard.go | 2 +- game/equipment.go | 4 +-- game/expbar.go | 2 +- game/gachareel.go | 2 +- game/game.go | 4 +-- game/gameover.go | 2 +- game/healthbar.go | 2 +- game/hearts.go | 2 +- game/hotbar.go | 2 +- game/interactprompt.go | 4 +-- game/letterbox.go | 4 +-- game/levelupscreen.go | 2 +- game/lives.go | 2 +- game/loadingtips.go | 2 +- game/lootpopup.go | 2 +- game/manabar.go | 2 +- game/menus_test.go | 2 +- game/minimap.go | 2 +- game/missionbrief.go | 2 +- game/optionsmenu.go | 4 +-- game/partyframes.go | 2 +- game/pressstart.go | 4 +-- game/questlog.go | 2 +- game/questtracker.go | 2 +- game/resultscreen.go | 2 +- game/rewards_test.go | 2 +- game/saveslots.go | 2 +- game/scoreboard.go | 2 +- game/shop.go | 2 +- game/skillbutton.go | 4 +-- game/skilltree.go | 4 +-- game/staminabar.go | 2 +- game/statuseffects.go | 2 +- game/targetselector.go | 2 +- game/timerhud.go | 2 +- game/titlescreen.go | 4 +-- game/treasurechest.go | 4 +-- game/turnorder.go | 2 +- go.mod | 3 ++- go.sum | 6 +++-- input/autocomplete.go | 2 +- input/button.go | 6 ++--- input/buttongroup.go | 4 +-- input/cheatcode.go | 4 +-- input/checkbox.go | 4 +-- input/checkboxgroup.go | 4 +-- input/colorslot.go | 2 +- input/combobox.go | 10 +++---- input/dpad.go | 4 +-- input/editabletext.go | 6 ++--- input/field.go | 2 +- input/fieldset.go | 2 +- input/holdbutton.go | 4 +-- input/joystick.go | 4 +-- input/knob.go | 2 +- input/multiselect.go | 10 +++---- input/nameentry.go | 2 +- input/pininput.go | 6 ++--- input/radio.go | 4 +-- input/radiogroup.go | 6 ++--- input/rangeslider.go | 4 +-- input/rating.go | 4 +-- input/segmented.go | 4 +-- input/select.go | 14 +++++----- input/slider.go | 12 ++++----- input/slotpicker.go | 4 +-- input/stepper.go | 4 +-- input/switch.go | 4 +-- input/textinput.go | 6 ++--- input/toggle.go | 4 +-- input/togglegroup.go | 4 +-- input/tokenfield.go | 2 +- input/virtualkeyboard.go | 2 +- internal/archtest/archtest_test.go | 4 +++ layout/appframe.go | 4 +-- layout/aspectbox.go | 2 +- layout/card.go | 2 +- layout/crtframe.go | 2 +- layout/divider.go | 2 +- layout/grid.go | 2 +- layout/handheld.go | 4 +-- layout/layers.go | 2 +- layout/layout.go | 2 +- layout/layout_test.go | 26 ++++++++++-------- layout/masonry.go | 2 +- layout/panel.go | 2 +- layout/resizable.go | 4 +-- layout/screen.go | 2 +- layout/scrollarea.go | 6 ++--- layout/section.go | 2 +- layout/split.go | 6 ++--- layout/statusbar.go | 2 +- layout/tiles.go | 2 +- layout/titlebar.go | 4 +-- layout/viewport.go | 2 +- layout/window.go | 2 +- media/audioplayer.go | 2 +- media/cartridge.go | 2 +- media/cassette.go | 2 +- media/equalizer.go | 2 +- media/filmstrip.go | 2 +- media/gallery.go | 2 +- media/imagecompare.go | 2 +- media/media.go | 4 +-- media/notes.go | 2 +- media/pianokeys.go | 4 +-- media/playlist.go | 2 +- media/recordbutton.go | 2 +- media/scrubber.go | 2 +- media/stepsequencer.go | 2 +- media/tracker.go | 2 +- media/transport.go | 2 +- media/tuner.go | 2 +- media/videoframe.go | 2 +- media/vinyl.go | 2 +- media/visualizer.go | 2 +- media/volume.go | 2 +- media/waveform.go | 2 +- navigation/backforward.go | 2 +- navigation/bottomnav.go | 4 +-- navigation/breadcrumbs.go | 4 +-- navigation/commandmenu.go | 6 ++--- navigation/commandpalette.go | 6 ++--- navigation/contextmenu.go | 4 +-- navigation/documenttabs.go | 6 ++--- navigation/dropdown.go | 2 +- navigation/levelselect.go | 2 +- navigation/menubar.go | 6 ++--- navigation/menus.go | 6 ++--- navigation/pagedots.go | 4 +-- navigation/pagination.go | 2 +- navigation/rail.go | 4 +-- navigation/sidebar.go | 4 +-- navigation/steps.go | 2 +- navigation/tabs.go | 4 +-- navigation/toc.go | 4 +-- navigation/toolbar.go | 2 +- navigation/verticaltabs.go | 2 +- navigation/worldmap.go | 4 +-- overlay/alertdialog.go | 4 +-- overlay/choiceprompt.go | 4 +-- overlay/coachmark.go | 4 +-- overlay/confirmdialog.go | 4 +-- overlay/dialog.go | 4 +-- overlay/dialoguebox.go | 2 +- overlay/drawer.go | 4 +-- overlay/handback_test.go | 42 ++++++++++++++++++++++++++++++ overlay/hovercard.go | 2 +- overlay/keyhints.go | 2 +- overlay/lightbox.go | 4 +-- overlay/notification.go | 2 +- overlay/overlay.go | 17 ++++++------ overlay/pausemenu.go | 4 +-- overlay/popconfirm.go | 2 +- overlay/popover.go | 4 +-- overlay/progressdialog.go | 4 +-- overlay/promptdialog.go | 6 ++--- overlay/sheet.go | 4 +-- overlay/spotlight.go | 4 +-- overlay/titlecard.go | 2 +- overlay/tooltip.go | 6 ++--- pixel/clock.go | 6 ++--- pixel/editor.go | 6 ++--- pixel/element.go | 26 +++++++++++++----- pixel/element_test.go | 6 ++--- pixel/pad.go | 4 +-- pixel/settings.go | 11 ++++---- project/assigneepicker.go | 4 +-- project/burndown.go | 2 +- project/checklist.go | 2 +- project/issuecard.go | 4 +-- project/labelpicker.go | 2 +- project/milestones.go | 2 +- project/pomodoro.go | 2 +- project/priority.go | 2 +- project/project.go | 2 +- project/questboard.go | 4 +-- project/roadmap.go | 2 +- project/sprintpanel.go | 2 +- project/taskitem.go | 2 +- project/tasklist.go | 2 +- project/timetracker.go | 2 +- project/workloadbar.go | 2 +- shop/bidticker.go | 2 +- shop/cart.go | 2 +- shop/checkout.go | 2 +- shop/coupon.go | 2 +- shop/currencyconvert.go | 2 +- shop/dailyreward.go | 2 +- shop/pricetag.go | 2 +- shop/pricingtiers.go | 4 +-- shop/quantity.go | 4 +-- shop/receipt.go | 2 +- shop/salebadge.go | 2 +- shop/shop.go | 2 +- shop/shopitem.go | 2 +- shop/tradewindow.go | 2 +- shop/vendingmachine.go | 2 +- shop/wallet.go | 2 +- tabletop/bingocard.go | 2 +- tabletop/cardhand.go | 4 +-- tabletop/chessboard.go | 2 +- tabletop/coinflip.go | 2 +- tabletop/deck.go | 4 +-- tabletop/dice.go | 2 +- tabletop/dicetray.go | 2 +- tabletop/domino.go | 2 +- tabletop/goboard.go | 2 +- tabletop/mahjongtile.go | 2 +- tabletop/meeple.go | 2 +- tabletop/playingcard.go | 2 +- tabletop/roulette.go | 2 +- tabletop/scoretrack.go | 2 +- tabletop/tictactoe.go | 2 +- text/blinktext.go | 2 +- text/code.go | 2 +- text/gradienttext.go | 2 +- text/heading.go | 2 +- text/highlight.go | 2 +- text/kbd.go | 2 +- text/link.go | 4 +-- text/marquee.go | 2 +- text/microtext.go | 2 +- text/odometer.go | 2 +- text/outlinetext.go | 2 +- text/paragraph.go | 2 +- text/quote.go | 2 +- text/rainbowtext.go | 2 +- text/scrambletext.go | 2 +- text/sevensegment.go | 2 +- text/shadowtext.go | 2 +- text/shaketext.go | 2 +- text/text.go | 2 +- text/typewriter.go | 4 +-- text/wavetext.go | 2 +- 532 files changed, 828 insertions(+), 735 deletions(-) create mode 100644 overlay/handback_test.go diff --git a/AGENTS.md b/AGENTS.md index 3c0abb0..e57c744 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,13 +8,13 @@ Progress truth: `TASKS.md`. Requirements: `PRD.md` and `docs/prd/components-*.md | Item | Value | | --- | --- | | Go | 1.27.1 | -| MyGo | `github.com/egoist/mygo v0.2.10`, pinned in go.mod | +| MyGo | `github.com/egoist/mygo v0.5.5`, pinned in go.mod | | Font | Ark Pixel Font 12px proportional zh_Hans v2026.09.25 (OFL-1.1, `pixel/OFL.txt`). Rebuild: `go run ./tools/fontpack -in ark-pixel-12px-proportional-zh_hans.bdf` (BDF sha256 `535f7024…8c86`) | | QR | `rsc.io/qr v0.2.0` (BSD-3), encoder behind `display.QRCode` | -| Web | `cmd/web` built with `GOOS=js GOARCH=wasm`; the browser runs the gallery under `ui.Tester` and blits frames to a canvas | +| Web | `cmd/web` built with `GOOS=js GOARCH=wasm`; the browser runs the gallery under `ui.Tester`, whose clock the host moves with real time (at most 500 ms a call), and blits frames to a canvas | | Hosting | Cloudflare Workers static assets from `web/dist`; code on Gitea `git.zacharyzhang.com` | -Read MyGo source before using an API: `$(go env GOMODCACHE)/github.com/egoist/mygo@v0.2.10` (`ui/*.go`, `docs/ui/*.md`). Never guess. +Read MyGo source before using an API: `$(go env GOMODCACHE)/github.com/egoist/mygo@v0.5.5` (`ui/*.go`, `docs/ui/*.md`). Never guess. ## Layout @@ -65,7 +65,7 @@ Imports point down only. Same-tier imports are forbidden. `internal/archtest` en - Read pointers and keys only while `base.IsDisabled()` is false, never by the `Disabled` option alone: `pixel.Shell` disables the base under Inert too, and a drag begun before stays pressed. MyGo still hands a key to a base that turned off this frame, so keys go through `pixel.Shortcut` (archtest bans an element's own `Shortcut`); a container that stays enabled, or a wrapper around an editor, also checks its own off state. `pixel.Editor` turns away text typed in a veil's first frame; read an edited value after building its editor, or gate the read. A `HandleInput` handler runs before the build learns the base is off: it records, and the build applies only while the base is enabled. - MyGo's value bases (`ToggleBase`, `SwitchBase`, `CheckboxBase`, `RadioBase`, `SegmentedBase`, `TabsBase`, `CollapsibleBase`) turn their value while built, before `Disabled` applies, so a key in a veil's first frame lands. Hand them a copy; keep only turns of an enabled base. - MyGo paints a disabled element and all inside it at half opacity, and only a disabled element reads as disabled to assistive technology. Disable only drawless bases, as `pixel.Shell` does. A container with drawn content stays enabled, parks its focus, ignores input and says `DISABLED` in its description. -- A press focuses the nearest focusable element under it, or nothing, and a focusable element keeps the click: clicks never reach its ancestors. A control that acts on a press is that focusable element. A `DialogBase` moves the focus inside as it opens and keeps Tab there, but only onto something focusable: make its panel focusable, ring off, and build its children in `pixel.Harbor(c, panel, …)` so controls turning off park their focus there and get it back. A closing overlay hands the focus back to its opener. While a press is on, `Hovered` is false for every other element: ask `Pressed`. +- A press focuses the nearest focusable element under it, or nothing, and a focusable element keeps the click: clicks never reach its ancestors. A control that acts on a press is that focusable element. A `DialogBase` moves the focus inside as it opens and keeps Tab there, but only onto something focusable: make its panel focusable, ring off, and build its children in `pixel.Harbor(c, panel, …)` so controls turning off park their focus there and get it back. A closing overlay hands the focus back to its opener: MyGo notes the opener after building the overlay, so focus an element as it appears with `pixel.AutoFocus`, never `AutoFocus` (archtest bans it). While a press is on, the elements around it stay `Hovered`: ask `Pressed`, and tell a press-given focus by focus arriving under the pointer. - Enter and Space click a focused clickable element unless a `Shortcut` takes them. Take them wherever a click reads the pointer's position. - Under `pixel.Settings.Inert` (a busy veil) `pixel.Shell` disables every base and gives the focus back after. A popup sits above any veil: a control closes its popup when disabled or inert. If the popup held the focus, focus the opener and `pixel.Park` it in that frame; tell it by `Focused` on the popup's controls, since MyGo links parents only after a frame. - Elements are known by their place among siblings. Wrap one whose earlier siblings come and go in `ui.Box(c).Key(k)`, or it loses focus and state. Rows the caller removes are keyed by a name that must be unique: panic on twins. @@ -77,7 +77,8 @@ Imports point down only. Same-tier imports are forbidden. `internal/archtest` en - Item slots look alike everywhere: draw them with `data.DrawSlot` or `data.ItemSlot`. - Fail fast: panic on programmer errors such as bad options. Never swallow an error. - Composites log state changes: `slog.Debug("8bit: ", …)`. Never log secrets, nor anything typed or copied: log counts. The gallery logs which demo spoke, not what it said. -- No cross-frame `*ui.Element` caching. No goroutines touching elements. +- A `ui.Element` is a handle that expires before the next build pass: never keep one across passes; bind a `ui.Handle` instead (`Handle.Bounds(c)` works between builds). No goroutines touching elements. +- `Context` methods answer only during a build: an input handler reads the clipboard through `c.Services()`. - Files under 500 lines. Comments: one line, ten words at most, only when needed. No dead code, no versioned APIs. ## Gallery entry @@ -87,7 +88,7 @@ Each component adds one `Entry` in `gallery/demos_.go`: ID, Name, Zh, ## Tests - `go vet ./... && go test ./...` before reporting done. -- Behavior tests drive `ui.Tester` and assert values. Timed components take `pixel.Settings.Now` from a test clock. `Tester.Focused(label)` is also true when an ancestor holds the focus. +- Behavior tests drive `ui.Tester` and assert values. Timed components take `pixel.Settings.Now` from a test clock. The Tester clock stands still until `Advance`; measure `pixel.Due()` against `tt.Now()`. `Tester.Focused(label)` is also true when an ancestor holds the focus. - `gallery` checks every entry under every palette: only palette colors, whole dots. Demos that call `pixel.Scope`, and the home and entry pages, may show other palettes. `PIXEL_SHOTS= go test ./gallery -run TestInvariants` writes PNGs for visual review. - `gallery` also sweeps all demos: keys, typing, clicks and the wheel as a veil falls log nothing; a clock stepping back crashes nothing and shows no new negative number; every Tab stop has a name; every gallery word has glyphs; the README's counts match the registry. - Web: `npm install && npm test` in `web/` builds `web/dist` and runs Playwright on a local server; `BASE_URL= npx playwright test` checks a deployed site. Tests read `window.__8bit` (`ready`, `stats()`, `texts()`, `page()`, `find(label)`, `log`). Every page must end without console or page errors; a test that expects some says so in an annotation. The budget test logs first frame, p50 and p95 (Go's share plus the canvas blit) and the wasm's brotli bytes, and holds them to PRD §6. diff --git a/README.md b/README.md index 7e98502..8257df1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Pixel-art components in the manner of 16-bit consoles, for [MyGo](https://github go get git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI@latest ``` -It needs Go 1.27.1 or later and builds on MyGo v0.2.10. MyGo's native UI needs nothing else; on Linux it runs on GTK 3. +It needs Go 1.27.1 or later and builds on MyGo v0.5.5. MyGo's native UI needs nothing else; on Linux it runs on GTK 3. ## A first window diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 2c4ef74..45397d0 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -4,7 +4,7 @@ | Work | Version | License | What it does here | | --- | --- | --- | --- | -| [MyGo](https://github.com/egoist/mygo) | v0.2.10 | MIT | Native UI toolkit the components build on | +| [MyGo](https://github.com/egoist/mygo) | v0.5.5 | MIT | Native UI toolkit the components build on | | [Ark Pixel Font](https://github.com/TakWolf/ark-pixel-font), 12px proportional zh_Hans | 2026.09.25 | SIL OFL 1.1 | The pixel font, packed into `pixel/font12.bin` | | [rsc.io/qr](https://pkg.go.dev/rsc.io/qr) | v0.2.0 | BSD-3-Clause | QR encoding for `display.QRCode` | | [go-text/typesetting](https://github.com/go-text/typesetting) | v0.3.5 | Unlicense or BSD-3-Clause; used under BSD-3-Clause | Text shaping inside MyGo | diff --git a/account/about.go b/account/about.go index 11e87ce..5acea27 100644 --- a/account/about.go +++ b/account/about.go @@ -17,7 +17,7 @@ type AboutOptions struct { } // About shows the title as a logo, version, then credits. -func About(c *ui.Context, title, version string, o AboutOptions) *ui.Element { +func About(c *ui.Context, title, version string, o AboutOptions) ui.Element { k := pixel.K(c) if version == "" { panic("account: About needs a version") diff --git a/account/accessibilityoptions.go b/account/accessibilityoptions.go index f931fdf..01a9361 100644 --- a/account/accessibilityoptions.go +++ b/account/accessibilityoptions.go @@ -22,7 +22,7 @@ type AccessibilityOptionsOptions struct { } type AccessibilityOptionsResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/account/account.go b/account/account.go index 8d5ede6..b93b307 100644 --- a/account/account.go +++ b/account/account.go @@ -24,14 +24,14 @@ func choice(name string, i *int, n int) { func words(k pixel.Kit, key string) []string { return strings.Fields(k.Msg(key)) } // caption is a line of micro text named for readers. -func caption(c *ui.Context, k pixel.Kit, s string, slot pixel.Slot) *ui.Element { +func caption(c *ui.Context, k pixel.Kit, s string, slot pixel.Slot) ui.Element { return ui.Box(c).Size(k.D(pixel.MicroWidth(s)), k.D(5)).Role(ui.RoleText).Label(s).Draw(func(p *ui.Painter, r ui.Rect) { k.Canvas(p, r).Micro(0, 0, s, slot, 1) }) } // mark draws s unnamed, beside the control that names it. -func mark(c *ui.Context, k pixel.Kit, s string, slot pixel.Slot, w int) *ui.Element { +func mark(c *ui.Context, k pixel.Kit, s string, slot pixel.Slot, w int) ui.Element { return ui.Box(c).Size(k.D(max(w, pixel.Measure(s))), k.D(pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) { k.Canvas(p, r).Text(0, 0, s, slot, 1) }) diff --git a/account/accountswitcher.go b/account/accountswitcher.go index a0a7872..fce7e62 100644 --- a/account/accountswitcher.go +++ b/account/accountswitcher.go @@ -15,7 +15,7 @@ type AccountSwitcherOptions struct { } type AccountSwitcherResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -36,7 +36,7 @@ func AccountSwitcher(c *ui.Context, text string, names [4]string, current *int, label += ", " + k.Msg("in_use") } // The shell draws the card; the base stays bare. - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Role(ui.RoleRadio).Label(label).Disabled(o.Disabled) }) if base.Clicked() && *current != i { diff --git a/account/charactercreator.go b/account/charactercreator.go index 43b8888..dbbeac0 100644 --- a/account/charactercreator.go +++ b/account/charactercreator.go @@ -113,7 +113,7 @@ type CharacterCreatorOptions struct { } type CharacterCreatorResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/account/controllermap.go b/account/controllermap.go index 370febf..5c85fad 100644 --- a/account/controllermap.go +++ b/account/controllermap.go @@ -37,7 +37,7 @@ var pad = []struct { type ControllerMapOptions struct{} // ControllerMap draws a pad; labels say what each button does. -func ControllerMap(c *ui.Context, text string, labels map[PadButton]string, o ControllerMapOptions) *ui.Element { +func ControllerMap(c *ui.Context, text string, labels map[PadButton]string, o ControllerMapOptions) ui.Element { k := pixel.K(c) var said []string for b := DPad; b <= Select; b++ { diff --git a/account/difficultyselect.go b/account/difficultyselect.go index 7bd6936..38af8ef 100644 --- a/account/difficultyselect.go +++ b/account/difficultyselect.go @@ -15,7 +15,7 @@ type DifficultySelectOptions struct { } type DifficultySelectResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/account/feedbackform.go b/account/feedbackform.go index a360812..21b40dc 100644 --- a/account/feedbackform.go +++ b/account/feedbackform.go @@ -17,7 +17,7 @@ type FeedbackFormOptions struct { } type FeedbackFormResult struct { - Element *ui.Element + Element ui.Element Sent bool } diff --git a/account/keybindings.go b/account/keybindings.go index 3cbb72b..da36975 100644 --- a/account/keybindings.go +++ b/account/keybindings.go @@ -38,7 +38,7 @@ type KeyBindingsOptions struct { } type KeyBindingsResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -64,7 +64,7 @@ func KeyBindings(c *ui.Context, text string, binds []Binding, o KeyBindingsOptio panic("account: KeyBindings has no bindings") } w, rh := pixel.Or("KeyBindings Width", o.Width, 200), pixel.LineHeight+4 - 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.RoleList).Label(text).Disabled(o.Disabled) }) kb := ui.Local(base, bindKey{}, func() capture { return capture{} }) diff --git a/account/loginform.go b/account/loginform.go index 2d1248f..f9d77d0 100644 --- a/account/loginform.go +++ b/account/loginform.go @@ -19,7 +19,7 @@ type LoginFormOptions struct { } type LoginFormResult struct { - Element *ui.Element + Element ui.Element Submitted bool } diff --git a/account/optioncycler.go b/account/optioncycler.go index 44d6fd1..72f5ceb 100644 --- a/account/optioncycler.go +++ b/account/optioncycler.go @@ -15,7 +15,7 @@ type OptionCyclerOptions struct { } type OptionCyclerResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -28,7 +28,7 @@ func OptionCycler(c *ui.Context, text string, options []string, selected *int, o longest = max(longest, pixel.Measure(s)) } w := pixel.Or("OptionCycler Width", o.Width, longest) - 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.RoleStepper).Label(text).Disabled(o.Disabled) }) n, old := len(options), *selected diff --git a/account/palettepicker.go b/account/palettepicker.go index a1afe29..625f8d0 100644 --- a/account/palettepicker.go +++ b/account/palettepicker.go @@ -13,7 +13,7 @@ type PalettePickerOptions struct { } type PalettePickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/account/passwordgrid.go b/account/passwordgrid.go index ddb3f24..18f2ea6 100644 --- a/account/passwordgrid.go +++ b/account/passwordgrid.go @@ -21,7 +21,7 @@ type PasswordGridOptions struct { } type PasswordGridResult struct { - Element *ui.Element + Element ui.Element // Submitted is END on a full password. Changed, Submitted bool } diff --git a/account/patchnotes.go b/account/patchnotes.go index 81ef727..1105466 100644 --- a/account/patchnotes.go +++ b/account/patchnotes.go @@ -19,7 +19,7 @@ type PatchNotesOptions struct { } // PatchNotes lists versions, each line tagged ADDED, FIXED or CHANGED. -func PatchNotes(c *ui.Context, title string, patches []Patch, o PatchNotesOptions) *ui.Element { +func PatchNotes(c *ui.Context, title string, patches []Patch, o PatchNotesOptions) ui.Element { k := pixel.K(c) w := pixel.Or("PatchNotes Width", o.Width, 240) // Tags draw in the main font, so any locale reads. diff --git a/account/playerselect.go b/account/playerselect.go index cb61a15..1792e7c 100644 --- a/account/playerselect.go +++ b/account/playerselect.go @@ -14,7 +14,7 @@ type PlayerSelectOptions struct { } type PlayerSelectResult struct { - Element *ui.Element + Element ui.Element // Chosen is Enter, Space or a click on a row. Changed, Chosen bool } diff --git a/account/profilecard.go b/account/profilecard.go index b04a918..132391a 100644 --- a/account/profilecard.go +++ b/account/profilecard.go @@ -30,7 +30,7 @@ type ProfileCardOptions struct { } // ProfileCard shows a player: face, name, level, title and stats. -func ProfileCard(c *ui.Context, p Profile, o ProfileCardOptions) *ui.Element { +func ProfileCard(c *ui.Context, p Profile, o ProfileCardOptions) ui.Element { k := pixel.K(c) if p.Level < 1 { panic(fmt.Sprintf("account: ProfileCard level %d", p.Level)) diff --git a/account/sessionlist.go b/account/sessionlist.go index 86b68d6..c683a0f 100644 --- a/account/sessionlist.go +++ b/account/sessionlist.go @@ -27,7 +27,7 @@ type SessionListOptions struct { } type SessionListResult struct { - Element *ui.Element + Element ui.Element // SignedOut is the session signed out this frame, else -1. SignedOut int } diff --git a/account/settingslist.go b/account/settingslist.go index b4d9947..f145513 100644 --- a/account/settingslist.go +++ b/account/settingslist.go @@ -24,7 +24,7 @@ type SettingsListOptions struct { } // SettingsList sets controls beside labels under section titles. -func SettingsList(c *ui.Context, text string, sections []SettingsSection, o SettingsListOptions) *ui.Element { +func SettingsList(c *ui.Context, text string, sections []SettingsSection, o SettingsListOptions) ui.Element { k := pixel.K(c) return ui.Column(c).Width(k.D(pixel.Or("SettingsList Width", o.Width, 240))).Gap(k.D(2)).Role(ui.RoleGroup).Label(text).Children(func() { for i, s := range sections { diff --git a/account/twofactor.go b/account/twofactor.go index 38f97f4..f7cf966 100644 --- a/account/twofactor.go +++ b/account/twofactor.go @@ -19,7 +19,7 @@ type TwoFactorOptions struct { } type TwoFactorResult struct { - Element *ui.Element + Element ui.Element // Submitted is the sixth box filled. Changed, Submitted bool } diff --git a/account/volumemixer.go b/account/volumemixer.go index 1ba9a10..ebe5189 100644 --- a/account/volumemixer.go +++ b/account/volumemixer.go @@ -20,7 +20,7 @@ type VolumeMixerOptions struct { } type VolumeMixerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/agent/agentcard.go b/agent/agentcard.go index fc9e5f1..5dd1a20 100644 --- a/agent/agentcard.go +++ b/agent/agentcard.go @@ -24,7 +24,7 @@ type AgentCardOptions struct { const cardW, cardH = 136, 48 // AgentCard is a party card: class, level, context as HP. -func AgentCard(c *ui.Context, a Agent, o AgentCardOptions) *ui.Element { +func AgentCard(c *ui.Context, a Agent, o AgentCardOptions) ui.Element { k := pixel.K(c) if a.Name == "" || a.Class == "" || a.Level < 1 || a.Window < 1 || a.Used < 0 || a.Used > a.Window { panic(fmt.Sprintf("agent: AgentCard %q, %q, LV %d, %d of %d tokens", a.Name, a.Class, a.Level, a.Used, a.Window)) diff --git a/agent/agentlog.go b/agent/agentlog.go index 9f9a51f..f659c30 100644 --- a/agent/agentlog.go +++ b/agent/agentlog.go @@ -41,7 +41,7 @@ type AgentLogOptions struct { type agentLogKey struct{} // AgentLog tells steps as a battle log, the newest typed. -func AgentLog(c *ui.Context, text, agent string, steps []LogStep, o AgentLogOptions) *ui.Element { +func AgentLog(c *ui.Context, text, agent string, steps []LogStep, o AgentLogOptions) ui.Element { k := pixel.K(c) if agent == "" { panic("agent: AgentLog has no agent") diff --git a/agent/agentstatus.go b/agent/agentstatus.go index e3133b2..c182637 100644 --- a/agent/agentstatus.go +++ b/agent/agentstatus.go @@ -19,7 +19,7 @@ type AgentStatusOptions struct { var halo = ring(17) // AgentStatus rings a portrait: idle, working, blocked or done. -func AgentStatus(c *ui.Context, text string, s Status, o AgentStatusOptions) *ui.Element { +func AgentStatus(c *ui.Context, text string, s Status, o AgentStatusOptions) ui.Element { k := pixel.K(c) face := portrait("AgentStatus", o.Portrait) word, slot := s.look(k) diff --git a/agent/approvalprompt.go b/agent/approvalprompt.go index c733172..0cceca0 100644 --- a/agent/approvalprompt.go +++ b/agent/approvalprompt.go @@ -20,7 +20,7 @@ type ApprovalPromptOptions struct { } type ApprovalPromptResult struct { - Element *ui.Element + Element ui.Element // Allowed or Denied is the answer given this frame. Allowed, Denied bool } @@ -81,7 +81,7 @@ func ApprovalPrompt(c *ui.Context, text, command string, o ApprovalPromptOptions } // sprite is a 12-dot icon, unnamed, in ink. -func sprite(c *ui.Context, k pixel.Kit, sp *pixel.Sprite, ink pixel.Slot) *ui.Element { +func sprite(c *ui.Context, k pixel.Kit, sp *pixel.Sprite, ink pixel.Slot) ui.Element { return ui.Box(c).Size(k.D(sp.W), k.D(sp.H)).Role(ui.RoleNone).Draw(func(p *ui.Painter, r ui.Rect) { k.Canvas(p, r).Sprite(0, 0, sp, 0, ink, 1) }) diff --git a/agent/checkpoints.go b/agent/checkpoints.go index 4cfe0c8..7fa6299 100644 --- a/agent/checkpoints.go +++ b/agent/checkpoints.go @@ -26,7 +26,7 @@ type CheckpointsOptions struct { } type CheckpointsResult struct { - Element *ui.Element + Element ui.Element // Restored is the checkpoint chosen this frame, else -1. Restored int } diff --git a/agent/costcoins.go b/agent/costcoins.go index 11183a5..7644418 100644 --- a/agent/costcoins.go +++ b/agent/costcoins.go @@ -15,7 +15,7 @@ type CostCoinsOptions struct { } // CostCoins stacks cost as gold dollars, silver dimes, bronze cents. -func CostCoins(c *ui.Context, text string, cents int, o CostCoinsOptions) *ui.Element { +func CostCoins(c *ui.Context, text string, cents int, o CostCoinsOptions) ui.Element { k := pixel.K(c) if cents < 0 { panic(fmt.Sprintf("agent: CostCoins of %d cents", cents)) diff --git a/agent/diffscroll.go b/agent/diffscroll.go index 701b28d..24cfafb 100644 --- a/agent/diffscroll.go +++ b/agent/diffscroll.go @@ -23,7 +23,7 @@ type DiffScrollOptions struct { } // DiffScroll unrolls a parchment of changed files, lines counted. -func DiffScroll(c *ui.Context, text string, files []FileDiff, o DiffScrollOptions) *ui.Element { +func DiffScroll(c *ui.Context, text string, files []FileDiff, o DiffScrollOptions) ui.Element { k := pixel.K(c) if len(files) == 0 { panic("agent: DiffScroll has no files") diff --git a/agent/memorycrystals.go b/agent/memorycrystals.go index 7bb83d8..2c6dfb1 100644 --- a/agent/memorycrystals.go +++ b/agent/memorycrystals.go @@ -18,7 +18,7 @@ type MemoryCrystalsOptions struct { } type MemoryCrystalsResult struct { - Element *ui.Element + Element ui.Element // Picked is the crystal chosen this frame, else -1. Picked int } diff --git a/agent/modelselect.go b/agent/modelselect.go index a0e37ad..fac8077 100644 --- a/agent/modelselect.go +++ b/agent/modelselect.go @@ -22,7 +22,7 @@ type ModelSelectOptions struct { } type ModelSelectResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/agent/planquest.go b/agent/planquest.go index d2ee017..3a0f1c7 100644 --- a/agent/planquest.go +++ b/agent/planquest.go @@ -22,7 +22,7 @@ type PlanQuestOptions struct { } // PlanQuest shows a plan's steps as quest objectives. -func PlanQuest(c *ui.Context, title string, steps []PlanStep, o PlanQuestOptions) *ui.Element { +func PlanQuest(c *ui.Context, title string, steps []PlanStep, o PlanQuestOptions) ui.Element { k := pixel.K(c) if title == "" || len(steps) == 0 { panic(fmt.Sprintf("agent: PlanQuest %q has %d steps", title, len(steps))) diff --git a/agent/promptbox.go b/agent/promptbox.go index 5b04dc0..88fd57f 100644 --- a/agent/promptbox.go +++ b/agent/promptbox.go @@ -20,7 +20,7 @@ type PromptBoxOptions struct { } type PromptBoxResult struct { - Element *ui.Element + Element ui.Element Changed bool // Cast is the prompt cast this frame, else empty. Cast string diff --git a/agent/subagentparty.go b/agent/subagentparty.go index 10c7248..41b208c 100644 --- a/agent/subagentparty.go +++ b/agent/subagentparty.go @@ -30,7 +30,7 @@ type partyRow struct { } // SubAgentParty draws the summoning tree, each agent with its status. -func SubAgentParty(c *ui.Context, text string, party []Member, o SubAgentPartyOptions) *ui.Element { +func SubAgentParty(c *ui.Context, text string, party []Member, o SubAgentPartyOptions) ui.Element { k := pixel.K(c) w := pixel.Or("SubAgentParty Width", o.Width, 200) var rows []partyRow diff --git a/agent/thinkingorb.go b/agent/thinkingorb.go index f3bcb80..c20ab49 100644 --- a/agent/thinkingorb.go +++ b/agent/thinkingorb.go @@ -24,7 +24,7 @@ var ( ) // ThinkingOrb is a crystal ball that swirls while thinking. -func ThinkingOrb(c *ui.Context, text string, thinking bool, o ThinkingOrbOptions) *ui.Element { +func ThinkingOrb(c *ui.Context, text string, thinking bool, o ThinkingOrbOptions) ui.Element { k := pixel.K(c) word := k.Msg("idle") if thinking { diff --git a/agent/tokenmana.go b/agent/tokenmana.go index e6b24d8..96012b1 100644 --- a/agent/tokenmana.go +++ b/agent/tokenmana.go @@ -14,7 +14,7 @@ type TokenManaOptions struct { } // TokenMana shows a token budget's remainder as MP. -func TokenMana(c *ui.Context, text string, used, budget int, o TokenManaOptions) *ui.Element { +func TokenMana(c *ui.Context, text string, used, budget int, o TokenManaOptions) ui.Element { k := pixel.K(c) if used < 0 || budget < 1 { panic(fmt.Sprintf("agent: TokenMana %d of %d", used, budget)) diff --git a/agent/toolcall.go b/agent/toolcall.go index 678cfb0..4cc7085 100644 --- a/agent/toolcall.go +++ b/agent/toolcall.go @@ -28,7 +28,7 @@ type ToolCallOptions struct { var glow = []pixel.Slot{pixel.Magic, pixel.Info, pixel.Accent} // ToolCall is a spell card: CAST, then OK or FAILED. -func ToolCall(c *ui.Context, tool string, out Outcome, o ToolCallOptions) *ui.Element { +func ToolCall(c *ui.Context, tool string, out Outcome, o ToolCallOptions) ui.Element { k := pixel.K(c) if tool == "" || out < Casting || out > Failed { panic(fmt.Sprintf("agent: ToolCall %q with outcome %d", tool, out)) diff --git a/chart/areachart.go b/chart/areachart.go index a743810..7408322 100644 --- a/chart/areachart.go +++ b/chart/areachart.go @@ -15,7 +15,7 @@ type AreaChartOptions struct { } // AreaChart draws a line with the area below it dithered. -func AreaChart(c *ui.Context, text string, labels []string, values []float64, o AreaChartOptions) *ui.Element { +func AreaChart(c *ui.Context, text string, labels []string, values []float64, o AreaChartOptions) ui.Element { k := pixel.K(c) w, h := size("AreaChart", o.Width, o.Height, 160, 80) if len(values) != len(labels) || len(values) < 2 { diff --git a/chart/axis.go b/chart/axis.go index 48efbba..7cd3f70 100644 --- a/chart/axis.go +++ b/chart/axis.go @@ -77,7 +77,7 @@ type AxisOptions struct { } // Axis draws ticks and micro labels, for any chart. -func Axis(c *ui.Context, o AxisOptions) *ui.Element { +func Axis(c *ui.Context, o AxisOptions) ui.Element { k := pixel.K(c) n := pixel.Or("Axis Length", o.Length, 120) marks := o.Labels diff --git a/chart/barchart.go b/chart/barchart.go index a488ad1..8a7dced 100644 --- a/chart/barchart.go +++ b/chart/barchart.go @@ -17,7 +17,7 @@ type BarChartOptions struct { } // BarChart stands bars on micro-font axes; hover reads a value. -func BarChart(c *ui.Context, text string, labels []string, values []float64, o BarChartOptions) *ui.Element { +func BarChart(c *ui.Context, text string, labels []string, values []float64, o BarChartOptions) ui.Element { k := pixel.K(c) if len(labels) != len(values) || len(values) == 0 { panic(fmt.Sprintf("chart: BarChart has %d labels for %d values", len(labels), len(values))) diff --git a/chart/boxplot.go b/chart/boxplot.go index 110e9cf..f8bee12 100644 --- a/chart/boxplot.go +++ b/chart/boxplot.go @@ -27,7 +27,7 @@ func quartile(sorted []float64, q float64) float64 { } // BoxPlot boxes each group's quartiles; whiskers reach its ends. -func BoxPlot(c *ui.Context, text string, groups []Series, o BoxPlotOptions) *ui.Element { +func BoxPlot(c *ui.Context, text string, groups []Series, o BoxPlotOptions) ui.Element { k := pixel.K(c) if len(groups) == 0 { panic("chart: BoxPlot has no groups") diff --git a/chart/bubblechart.go b/chart/bubblechart.go index 7af90ad..df92e86 100644 --- a/chart/bubblechart.go +++ b/chart/bubblechart.go @@ -16,7 +16,7 @@ type BubbleChartOptions struct { } // BubbleChart draws points as discs, area by Size. -func BubbleChart(c *ui.Context, text string, points []Point, o BubbleChartOptions) *ui.Element { +func BubbleChart(c *ui.Context, text string, points []Point, o BubbleChartOptions) ui.Element { k := pixel.K(c) w, h := size("BubbleChart", o.Width, o.Height, 160, 80) f := fitPoints(points, w, h, 8) diff --git a/chart/bulletchart.go b/chart/bulletchart.go index 616f652..574c34a 100644 --- a/chart/bulletchart.go +++ b/chart/bulletchart.go @@ -14,7 +14,7 @@ type BulletChartOptions struct { } // BulletChart lays a bar on rising ranges and a target. -func BulletChart(c *ui.Context, text string, value, target float64, ranges []float64, o BulletChartOptions) *ui.Element { +func BulletChart(c *ui.Context, text string, value, target float64, ranges []float64, o BulletChartOptions) ui.Element { k := pixel.K(c) if len(ranges) == 0 { panic("chart: BulletChart needs ranges") diff --git a/chart/calendarheatmap.go b/chart/calendarheatmap.go index dd392e2..c349e71 100644 --- a/chart/calendarheatmap.go +++ b/chart/calendarheatmap.go @@ -20,7 +20,7 @@ type CalendarHeatmapOptions struct { const day = 5 // CalendarHeatmap rates a year's UTC days 0–4, week by week. -func CalendarHeatmap(c *ui.Context, text string, year int, level func(d time.Time) int, o CalendarHeatmapOptions) *ui.Element { +func CalendarHeatmap(c *ui.Context, text string, year int, level func(d time.Time) int, o CalendarHeatmapOptions) ui.Element { k := pixel.K(c) ink := o.Slot if ink == 0 { diff --git a/chart/candlestick.go b/chart/candlestick.go index e0faea4..a15b820 100644 --- a/chart/candlestick.go +++ b/chart/candlestick.go @@ -21,7 +21,7 @@ type CandlestickOptions struct { } // Candlestick draws wicks and bodies: up Good, down Bad. -func Candlestick(c *ui.Context, text string, candles []Candle, o CandlestickOptions) *ui.Element { +func Candlestick(c *ui.Context, text string, candles []Candle, o CandlestickOptions) ui.Element { k := pixel.K(c) if len(candles) == 0 { panic("chart: Candlestick has no candles") diff --git a/chart/crosshair.go b/chart/crosshair.go index f3d2a11..e56931c 100644 --- a/chart/crosshair.go +++ b/chart/crosshair.go @@ -12,7 +12,7 @@ type CrosshairOptions struct { } // Crosshair is a line chart; hover crosses the nearest point. -func Crosshair(c *ui.Context, text string, labels []string, series []Series, o CrosshairOptions) *ui.Element { +func Crosshair(c *ui.Context, text string, labels []string, series []Series, o CrosshairOptions) ui.Element { k := pixel.K(c) w, h := size("Crosshair", o.Width, o.Height, 160, 80) p := fitSeries(labels, series, w, h) diff --git a/chart/donutchart.go b/chart/donutchart.go index a077884..5b88fbf 100644 --- a/chart/donutchart.go +++ b/chart/donutchart.go @@ -12,7 +12,7 @@ type DonutChartOptions struct { } // DonutChart is a pie with a hole holding the total. -func DonutChart(c *ui.Context, text string, slices []Slice, o DonutChartOptions) *ui.Element { +func DonutChart(c *ui.Context, text string, slices []Slice, o DonutChartOptions) ui.Element { k := pixel.K(c) n := pixel.Or("DonutChart Size", o.Size, 64) total, cuts := shares(slices) diff --git a/chart/funnel.go b/chart/funnel.go index 211795a..b64be1c 100644 --- a/chart/funnel.go +++ b/chart/funnel.go @@ -14,7 +14,7 @@ type FunnelOptions struct { } // Funnel narrows stage by stage, each bar with its count. -func Funnel(c *ui.Context, text string, stages []Slice, o FunnelOptions) *ui.Element { +func Funnel(c *ui.Context, text string, stages []Slice, o FunnelOptions) ui.Element { k := pixel.K(c) if len(stages) == 0 { panic("chart: Funnel has no stages") diff --git a/chart/gauge.go b/chart/gauge.go index b46f78c..820fef1 100644 --- a/chart/gauge.go +++ b/chart/gauge.go @@ -17,7 +17,7 @@ type GaugeOptions struct { } // Gauge needles a half dial and prints the value. -func Gauge(c *ui.Context, text string, value float64, o GaugeOptions) *ui.Element { +func Gauge(c *ui.Context, text string, value float64, o GaugeOptions) ui.Element { k := pixel.K(c) span([]float64{o.Min, o.Max, value}) lo, hi := o.Min, o.Max diff --git a/chart/heatmap.go b/chart/heatmap.go index b79fe4c..20c4c05 100644 --- a/chart/heatmap.go +++ b/chart/heatmap.go @@ -16,7 +16,7 @@ type HeatmapOptions struct { } // Heatmap shades cells by dither level between two slots. -func Heatmap(c *ui.Context, text string, rows, cols []string, values [][]float64, o HeatmapOptions) *ui.Element { +func Heatmap(c *ui.Context, text string, rows, cols []string, values [][]float64, o HeatmapOptions) ui.Element { k := pixel.K(c) if len(values) != len(rows) { panic(fmt.Sprintf("chart: Heatmap has %d rows of values for %d rows", len(values), len(rows))) diff --git a/chart/histogram.go b/chart/histogram.go index 3e164c5..3f09cae 100644 --- a/chart/histogram.go +++ b/chart/histogram.go @@ -17,7 +17,7 @@ type HistogramOptions struct { } // Histogram counts values into bins, side by side. -func Histogram(c *ui.Context, text string, values []float64, o HistogramOptions) *ui.Element { +func Histogram(c *ui.Context, text string, values []float64, o HistogramOptions) ui.Element { k := pixel.K(c) n := pixel.Or("Histogram Bins", o.Bins, 8) w, h := size("Histogram", o.Width, o.Height, 160, 80) diff --git a/chart/horizontalbar.go b/chart/horizontalbar.go index 1626630..653a82d 100644 --- a/chart/horizontalbar.go +++ b/chart/horizontalbar.go @@ -16,7 +16,7 @@ type HorizontalBarOptions struct { } // HorizontalBar lays labeled bars across, longest to the edge. -func HorizontalBar(c *ui.Context, text string, labels []string, values []float64, o HorizontalBarOptions) *ui.Element { +func HorizontalBar(c *ui.Context, text string, labels []string, values []float64, o HorizontalBarOptions) ui.Element { k := pixel.K(c) if len(labels) != len(values) || len(values) == 0 { panic(fmt.Sprintf("chart: HorizontalBar has %d labels for %d values", len(labels), len(values))) diff --git a/chart/legend.go b/chart/legend.go index 50d5628..f99799d 100644 --- a/chart/legend.go +++ b/chart/legend.go @@ -14,7 +14,7 @@ type LegendOptions struct { } type LegendResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -29,7 +29,7 @@ func Legend(c *ui.Context, text string, series []Series, hidden []bool, o Legend row.Children(func() { for i, s := range series { shown := !hidden[i] - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ToggleBase(c, &shown).Label(s.Name).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ToggleBase(c, &shown).Label(s.Name).Disabled(o.Disabled) }) // The base turns before it learns it is off. if base.Changed() && !base.IsDisabled() { hidden[i], res.Changed = !shown, true diff --git a/chart/linechart.go b/chart/linechart.go index 93a34de..008d6ce 100644 --- a/chart/linechart.go +++ b/chart/linechart.go @@ -14,7 +14,7 @@ type LineChartOptions struct { } // LineChart draws series as Bresenham lines with square markers. -func LineChart(c *ui.Context, text string, labels []string, series []Series, o LineChartOptions) *ui.Element { +func LineChart(c *ui.Context, text string, labels []string, series []Series, o LineChartOptions) ui.Element { k := pixel.K(c) w, h := size("LineChart", o.Width, o.Height, 160, 80) p := fitSeries(labels, series, w, h) diff --git a/chart/piechart.go b/chart/piechart.go index fdce1ad..400348a 100644 --- a/chart/piechart.go +++ b/chart/piechart.go @@ -12,7 +12,7 @@ type PieChartOptions struct { } // PieChart rasterizes one sector per slice, dot by dot. -func PieChart(c *ui.Context, text string, slices []Slice, o PieChartOptions) *ui.Element { +func PieChart(c *ui.Context, text string, slices []Slice, o PieChartOptions) ui.Element { k := pixel.K(c) n := pixel.Or("PieChart Size", o.Size, 64) _, cuts := shares(slices) diff --git a/chart/polararea.go b/chart/polararea.go index a06ecae..37424c4 100644 --- a/chart/polararea.go +++ b/chart/polararea.go @@ -12,7 +12,7 @@ type PolarAreaOptions struct { } // PolarArea gives slices equal turns, reaching out by value. -func PolarArea(c *ui.Context, text string, slices []Slice, o PolarAreaOptions) *ui.Element { +func PolarArea(c *ui.Context, text string, slices []Slice, o PolarAreaOptions) ui.Element { k := pixel.K(c) n := pixel.Or("PolarArea Size", o.Size, 64) shares(slices) diff --git a/chart/progressring.go b/chart/progressring.go index fb14f00..0af6eb3 100644 --- a/chart/progressring.go +++ b/chart/progressring.go @@ -16,7 +16,7 @@ type ProgressRingOptions struct { } // ProgressRing fills a ring clockwise from twelve, in pixel arcs. -func ProgressRing(c *ui.Context, text string, value float64, o ProgressRingOptions) *ui.Element { +func ProgressRing(c *ui.Context, text string, value float64, o ProgressRingOptions) ui.Element { k := pixel.K(c) if !(value >= 0 && value <= 1) { panic(fmt.Sprintf("chart: ProgressRing value %v outside 0–1", value)) diff --git a/chart/pyramid.go b/chart/pyramid.go index 8df416b..eb7f3a2 100644 --- a/chart/pyramid.go +++ b/chart/pyramid.go @@ -14,7 +14,7 @@ type PyramidOptions struct { } // Pyramid sets two series back to back by group. -func Pyramid(c *ui.Context, text string, groups []string, left, right Series, o PyramidOptions) *ui.Element { +func Pyramid(c *ui.Context, text string, groups []string, left, right Series, o PyramidOptions) ui.Element { k := pixel.K(c) if len(left.Values) != len(groups) || len(right.Values) != len(groups) { panic(fmt.Sprintf("chart: Pyramid has %d groups for %d and %d values", len(groups), len(left.Values), len(right.Values))) diff --git a/chart/radarchart.go b/chart/radarchart.go index 54bcddd..11dc131 100644 --- a/chart/radarchart.go +++ b/chart/radarchart.go @@ -18,7 +18,7 @@ type RadarChartOptions struct { } // RadarChart draws a stat polygon on a web of axes. -func RadarChart(c *ui.Context, text string, axes []string, values []float64, o RadarChartOptions) *ui.Element { +func RadarChart(c *ui.Context, text string, axes []string, values []float64, o RadarChartOptions) ui.Element { k := pixel.K(c) if len(axes) != len(values) || len(axes) < 3 { panic(fmt.Sprintf("chart: RadarChart has %d axes for %d values", len(axes), len(values))) diff --git a/chart/scatterplot.go b/chart/scatterplot.go index 66ffed9..e7eb1bb 100644 --- a/chart/scatterplot.go +++ b/chart/scatterplot.go @@ -13,7 +13,7 @@ type ScatterPlotOptions struct { } // ScatterPlot puts each point as a 2×2 square. -func ScatterPlot(c *ui.Context, text string, points []Point, o ScatterPlotOptions) *ui.Element { +func ScatterPlot(c *ui.Context, text string, points []Point, o ScatterPlotOptions) ui.Element { k := pixel.K(c) w, h := size("ScatterPlot", o.Width, o.Height, 160, 80) f := fitPoints(points, w, h, 2) diff --git a/chart/sparkline.go b/chart/sparkline.go index 2a2f1ec..b46c6b3 100644 --- a/chart/sparkline.go +++ b/chart/sparkline.go @@ -13,7 +13,7 @@ type SparklineOptions struct { } // Sparkline is a line one text line high, end marked. -func Sparkline(c *ui.Context, text string, values []float64, o SparklineOptions) *ui.Element { +func Sparkline(c *ui.Context, text string, values []float64, o SparklineOptions) ui.Element { k := pixel.K(c) w := pixel.Or("Sparkline Width", o.Width, 48) if len(values) < 2 { diff --git a/chart/spectrumbars.go b/chart/spectrumbars.go index 4fa730b..1b11c50 100644 --- a/chart/spectrumbars.go +++ b/chart/spectrumbars.go @@ -24,7 +24,7 @@ type peaks struct { } // SpectrumBars lights segmented bars; peak dots fall behind them. -func SpectrumBars(c *ui.Context, text string, levels []float64, o SpectrumBarsOptions) *ui.Element { +func SpectrumBars(c *ui.Context, text string, levels []float64, o SpectrumBarsOptions) ui.Element { k := pixel.K(c) n := pixel.Or("SpectrumBars Segments", o.Segments, 10) for _, l := range levels { diff --git a/chart/stackedbar.go b/chart/stackedbar.go index 159fddc..1160b8e 100644 --- a/chart/stackedbar.go +++ b/chart/stackedbar.go @@ -14,7 +14,7 @@ type StackedBarOptions struct { } // StackedBar stacks each category's series, one slot per series. -func StackedBar(c *ui.Context, text string, labels []string, series []Series, o StackedBarOptions) *ui.Element { +func StackedBar(c *ui.Context, text string, labels []string, series []Series, o StackedBarOptions) ui.Element { k := pixel.K(c) if len(labels) == 0 { panic("chart: StackedBar has no labels") diff --git a/chart/stepchart.go b/chart/stepchart.go index 458bdaa..26d0a64 100644 --- a/chart/stepchart.go +++ b/chart/stepchart.go @@ -13,7 +13,7 @@ type StepChartOptions struct { } // StepChart holds each value flat, then steps to the next. -func StepChart(c *ui.Context, text string, labels []string, values []float64, o StepChartOptions) *ui.Element { +func StepChart(c *ui.Context, text string, labels []string, values []float64, o StepChartOptions) ui.Element { k := pixel.K(c) w, h := size("StepChart", o.Width, o.Height, 160, 80) p := fitSeries(labels, []Series{{Name: text, Values: values}}, w, h) diff --git a/chart/treemap.go b/chart/treemap.go index b136311..0ec7306 100644 --- a/chart/treemap.go +++ b/chart/treemap.go @@ -17,7 +17,7 @@ type TreemapOptions struct { type box struct{ x, y, w, h int } // Treemap tiles slices by value in squarified rectangles. -func Treemap(c *ui.Context, text string, slices []Slice, o TreemapOptions) *ui.Element { +func Treemap(c *ui.Context, text string, slices []Slice, o TreemapOptions) ui.Element { k := pixel.K(c) w, h := size("Treemap", o.Width, o.Height, 160, 80) total, _ := shares(slices) diff --git a/chart/waterfall.go b/chart/waterfall.go index 6c1fe30..c1fe913 100644 --- a/chart/waterfall.go +++ b/chart/waterfall.go @@ -14,7 +14,7 @@ type WaterfallOptions struct { } // Waterfall walks a running total: Good up, Bad down. -func Waterfall(c *ui.Context, text string, labels []string, deltas []float64, o WaterfallOptions) *ui.Element { +func Waterfall(c *ui.Context, text string, labels []string, deltas []float64, o WaterfallOptions) ui.Element { k := pixel.K(c) if len(labels) != len(deltas) || len(deltas) == 0 { panic(fmt.Sprintf("chart: Waterfall has %d labels for %d steps", len(labels), len(deltas))) diff --git a/chart/winloss.go b/chart/winloss.go index 724b418..4b95f24 100644 --- a/chart/winloss.go +++ b/chart/winloss.go @@ -11,7 +11,7 @@ import ( type WinLossOptions struct{} // WinLoss rows blocks: wins up, losses down, draws dashed. -func WinLoss(c *ui.Context, text string, results []int, o WinLossOptions) *ui.Element { +func WinLoss(c *ui.Context, text string, results []int, o WinLossOptions) ui.Element { k := pixel.K(c) return ui.Box(c).Size(k.D(len(results)*5), k.D(pixel.LineHeight)).Role(ui.RoleImage).Label(text). Description(record(results)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/chat/attachmentchip.go b/chat/attachmentchip.go index 4b2c1d4..61a6d1e 100644 --- a/chat/attachmentchip.go +++ b/chat/attachmentchip.go @@ -21,7 +21,7 @@ type AttachmentChipOptions struct { } type AttachmentChipResult struct { - Element *ui.Element + Element ui.Element Removed bool } diff --git a/chat/callcontrols.go b/chat/callcontrols.go index 121d0e9..f24ad4f 100644 --- a/chat/callcontrols.go +++ b/chat/callcontrols.go @@ -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) } diff --git a/chat/channellist.go b/chat/channellist.go index 4aa183e..36dc4da 100644 --- a/chat/channellist.go +++ b/chat/channellist.go @@ -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 } diff --git a/chat/chat.go b/chat/chat.go index 6bc3388..129d5d6 100644 --- a/chat/chat.go +++ b/chat/chat.go @@ -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)) diff --git a/chat/chat_test.go b/chat/chat_test.go index 2f50ab0..13c613c 100644 --- a/chat/chat_test.go +++ b/chat/chat_test.go @@ -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) diff --git a/chat/chatlist.go b/chat/chatlist.go index b9c4e7c..bd76570 100644 --- a/chat/chatlist.go +++ b/chat/chatlist.go @@ -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 } diff --git a/chat/chatlog.go b/chat/chatlog.go index 562b592..654451a 100644 --- a/chat/chatlog.go +++ b/chat/chatlog.go @@ -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)) diff --git a/chat/composer.go b/chat/composer.go index 0449015..0ab9df6 100644 --- a/chat/composer.go +++ b/chat/composer.go @@ -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 diff --git a/chat/dateseparator.go b/chat/dateseparator.go index d4a6bb7..409d0f7 100644 --- a/chat/dateseparator.go +++ b/chat/dateseparator.go @@ -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. diff --git a/chat/emojipicker.go b/chat/emojipicker.go index 9b0fe8d..fab45df 100644 --- a/chat/emojipicker.go +++ b/chat/emojipicker.go @@ -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 } diff --git a/chat/letter.go b/chat/letter.go index af89307..6ebaecc 100644 --- a/chat/letter.go +++ b/chat/letter.go @@ -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 { diff --git a/chat/linkpreview.go b/chat/linkpreview.go index 54b1826..11ea628 100644 --- a/chat/linkpreview.go +++ b/chat/linkpreview.go @@ -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) { diff --git a/chat/mailbox.go b/chat/mailbox.go index 3d38be5..75279c6 100644 --- a/chat/mailbox.go +++ b/chat/mailbox.go @@ -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 diff --git a/chat/mailreader.go b/chat/mailreader.go index 8440453..46ca9a7 100644 --- a/chat/mailreader.go +++ b/chat/mailreader.go @@ -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() { diff --git a/chat/memberlist.go b/chat/memberlist.go index 10b9b97..22a9a87 100644 --- a/chat/memberlist.go +++ b/chat/memberlist.go @@ -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 diff --git a/chat/mentioninput.go b/chat/mentioninput.go index 252f18f..70928a9 100644 --- a/chat/mentioninput.go +++ b/chat/mentioninput.go @@ -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 diff --git a/chat/notificationbell.go b/chat/notificationbell.go index 3aa510b..b3aaee6 100644 --- a/chat/notificationbell.go +++ b/chat/notificationbell.go @@ -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} }) diff --git a/chat/poll.go b/chat/poll.go index 0ef9caa..bdf0000 100644 --- a/chat/poll.go +++ b/chat/poll.go @@ -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) diff --git a/chat/reactions.go b/chat/reactions.go index f9a1208..3b32906 100644 --- a/chat/reactions.go +++ b/chat/reactions.go @@ -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() { diff --git a/chat/readreceipt.go b/chat/readreceipt.go index d3424aa..2692b25 100644 --- a/chat/readreceipt.go +++ b/chat/readreceipt.go @@ -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 { diff --git a/chat/speechbubble.go b/chat/speechbubble.go index caeefe1..cef00dd 100644 --- a/chat/speechbubble.go +++ b/chat/speechbubble.go @@ -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. diff --git a/chat/stickerpicker.go b/chat/stickerpicker.go index d52d617..ebca8e7 100644 --- a/chat/stickerpicker.go +++ b/chat/stickerpicker.go @@ -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 } diff --git a/chat/systemmessage.go b/chat/systemmessage.go index 43dae17..c857ac0 100644 --- a/chat/systemmessage.go +++ b/chat/systemmessage.go @@ -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 { diff --git a/chat/typingindicator.go b/chat/typingindicator.go index 7e83635..4f615b8 100644 --- a/chat/typingindicator.go +++ b/chat/typingindicator.go @@ -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 { diff --git a/chat/unreaddivider.go b/chat/unreaddivider.go index ac671e7..e7e47b3 100644 --- a/chat/unreaddivider.go +++ b/chat/unreaddivider.go @@ -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) } diff --git a/chat/usercard.go b/chat/usercard.go index a3e644c..21e8d6b 100644 --- a/chat/usercard.go +++ b/chat/usercard.go @@ -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 } diff --git a/chat/voicemeter.go b/chat/voicemeter.go index 3960d2c..b86392a 100644 --- a/chat/voicemeter.go +++ b/chat/voicemeter.go @@ -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)) diff --git a/cmd/web/main.go b/cmd/web/main.go index 94daf6a..6a25cd6 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -20,6 +20,9 @@ import ( // dot is DIPs per dot; each dot is one pixel. const dot = gallery.Dot +// catchUp caps the replay after a hidden tab. +const catchUp = 500 * time.Millisecond + type host struct { app *gallery.App tt *ui.Tester @@ -29,10 +32,14 @@ type host struct { over bool dead bool began time.Time + // wall is when the Tester clock last caught up. + wall time.Time } func main() { h := &host{app: gallery.New("/")} + // Components keep real time while the Tester clock lags. + h.app.SetClock(time.Now) api := js.Global().Get("Object").New() export := func(name string, fn func(args []js.Value) any) { api.Set(name, js.FuncOf(func(_ js.Value, args []js.Value) any { return h.guard(name, args, fn) })) @@ -84,6 +91,7 @@ func (h *host) guard(name string, args []js.Value, fn func([]js.Value) any) (res // An empty clipboard after a call means nothing was copied. if h.tt != nil { h.tt.SetClipboard("") + h.sync() } pixel.ResetDue() h.began = time.Now() @@ -104,7 +112,7 @@ func (h *host) crash(why string) (res any) { } }() h.tt = ui.NewTester(func(c *ui.Context) { - pixel.Use(c, pixel.Settings{Palette: pixel.WinterNight, Dot: dot}) + pixel.Use(c, pixel.Settings{Palette: pixel.WinterNight, Dot: dot, Now: time.Now}) ui.Column(c).Fill().Center().Children(func() { if feedback.ErrorState(c, why, feedback.ErrorStateOptions{}).Continue { js.Global().Get("location").Call("reload") @@ -112,6 +120,7 @@ func (h *host) crash(why string) (res any) { }) }, h.w*dot, h.h*dot) h.tt.SetScale(1.0 / dot) + h.wall = time.Now() return h.out() } @@ -123,10 +132,18 @@ func (h *host) start(a []js.Value) any { } h.tt = ui.NewTester(h.app.View, h.w*dot, h.h*dot) h.tt.SetScale(1.0 / dot) + h.wall = time.Now() slog.Info("8bit: start", "dots", fmt.Sprintf("%dx%d", h.w, h.h), "path", h.app.Path()) return h.out() } +// sync moves the Tester clock on with the real one. +func (h *host) sync() { + now := time.Now() + h.tt.Advance(min(now.Sub(h.wall), catchUp)) + h.wall = now +} + func (h *host) size(w, ht int) { h.w, h.h = w, ht h.tt.SetSize(w*dot, ht*dot) @@ -169,7 +186,8 @@ func (h *host) out() any { took := float64(time.Since(h.began).Microseconds()) / 1000 res := map[string]any{"w": img.Rect.Dx(), "h": img.Rect.Dy(), "pix": pix, "path": h.app.Path(), "cursor": int(h.tt.Cursor()), "took": took} if due := pixel.Due(); !due.IsZero() { - res["due"] = float64(time.Until(due).Milliseconds()) + // Asked on the Tester clock, which may lag. + res["due"] = float64(due.Sub(h.tt.Now()).Milliseconds()) } if r, ok := h.tt.TextCaret(); ok { res["caret"] = []any{float64(r.X / dot), float64(r.Y / dot), float64(r.H / dot)} diff --git a/code/breakpoints.go b/code/breakpoints.go index 30a2e5a..0eacaf8 100644 --- a/code/breakpoints.go +++ b/code/breakpoints.go @@ -16,7 +16,7 @@ type BreakpointsOptions struct { } type BreakpointsResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/code/codeblock.go b/code/codeblock.go index fbb1551..a4fabbd 100644 --- a/code/codeblock.go +++ b/code/codeblock.go @@ -18,7 +18,7 @@ type CodeBlockOptions struct { } type CodeBlockResult struct { - Element *ui.Element + Element ui.Element Copied bool } diff --git a/code/commandhistory.go b/code/commandhistory.go index e641764..7bd705f 100644 --- a/code/commandhistory.go +++ b/code/commandhistory.go @@ -17,7 +17,7 @@ type CommandHistoryOptions struct { } type CommandHistoryResult struct { - Element *ui.Element + Element ui.Element // Rerun is Enter or a double click on a command. Changed, Rerun bool } diff --git a/code/crashscreen.go b/code/crashscreen.go index 72665d7..3dc0f96 100644 --- a/code/crashscreen.go +++ b/code/crashscreen.go @@ -16,7 +16,7 @@ type CrashScreenOptions struct { } type CrashScreenResult struct { - Element *ui.Element + Element ui.Element Continue bool } diff --git a/code/debuggrid.go b/code/debuggrid.go index b02565f..2b6ba45 100644 --- a/code/debuggrid.go +++ b/code/debuggrid.go @@ -14,7 +14,7 @@ type DebugGridOptions struct { } // DebugGrid rules content with the dot grid and rulers. -func DebugGrid(c *ui.Context, o DebugGridOptions, content func()) *ui.Element { +func DebugGrid(c *ui.Context, o DebugGridOptions, content func()) ui.Element { k := pixel.K(c) step := pixel.Or("DebugGrid Step", o.Step, 8) if step < 2 || step > 32 { diff --git a/code/diffview.go b/code/diffview.go index d3a52a0..1f9d1a1 100644 --- a/code/diffview.go +++ b/code/diffview.go @@ -20,7 +20,7 @@ type change struct { } // DiffView marks lines removed with -, added with +. -func DiffView(c *ui.Context, text, before, after string, o DiffViewOptions) *ui.Element { +func DiffView(c *ui.Context, text, before, after string, o DiffViewOptions) ui.Element { k := pixel.K(c) changes := diff(strings.Split(tidy(before), "\n"), strings.Split(tidy(after), "\n")) // Rows share the longest width, so tints line up. diff --git a/code/fpsmeter.go b/code/fpsmeter.go index db645c5..bd9ef49 100644 --- a/code/fpsmeter.go +++ b/code/fpsmeter.go @@ -16,7 +16,7 @@ type FPSMeterOptions struct { } // FPSMeter draws frame times as bars, newest right, with percentiles. -func FPSMeter(c *ui.Context, text string, frames []time.Duration, o FPSMeterOptions) *ui.Element { +func FPSMeter(c *ui.Context, text string, frames []time.Duration, o FPSMeterOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("FPSMeter Width", o.Width, 120), pixel.Or("FPSMeter Height", o.Height, 40) for _, f := range frames { diff --git a/code/glyphmap.go b/code/glyphmap.go index edd39c2..edbd3b6 100644 --- a/code/glyphmap.go +++ b/code/glyphmap.go @@ -16,7 +16,7 @@ type GlyphMapOptions struct { } type GlyphMapResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/code/guards_test.go b/code/guards_test.go index af52d88..f78d379 100644 --- a/code/guards_test.go +++ b/code/guards_test.go @@ -115,9 +115,9 @@ func TestCrashScreenStepsAtFourFPS(t *testing.T) { tt := view(600, 300, wd, func(c *ui.Context) { code.CrashScreen(c, "panic: oops", nil, code.CrashScreenOptions{}) }) - // Paint asks for the next step on the real clock. + // Paint asks for the next step on the frame clock. pixel.ResetDue() - before := time.Now() + before := tt.Now() tt.Frame() if d := pixel.Due().Sub(before); d < time.Second/4 || d >= time.Second/2 { t.Fatalf("the next blink step is %v away, want a quarter second", d) diff --git a/code/hexviewer.go b/code/hexviewer.go index 125eb90..bb732b9 100644 --- a/code/hexviewer.go +++ b/code/hexviewer.go @@ -16,7 +16,7 @@ type HexViewerOptions struct { } type HexViewerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/code/logviewer.go b/code/logviewer.go index b573316..05a362a 100644 --- a/code/logviewer.go +++ b/code/logviewer.go @@ -40,7 +40,7 @@ type LogViewerOptions struct { } type LogViewerResult struct { - Element *ui.Element + Element ui.Element // Changed is the level filter. Changed bool } diff --git a/code/memorymap.go b/code/memorymap.go index 523c4ff..790c7cf 100644 --- a/code/memorymap.go +++ b/code/memorymap.go @@ -21,7 +21,7 @@ type MemoryMapOptions struct { } // MemoryMap draws each bank as a block filled by use. -func MemoryMap(c *ui.Context, text string, banks []Bank, o MemoryMapOptions) *ui.Element { +func MemoryMap(c *ui.Context, text string, banks []Bank, o MemoryMapOptions) ui.Element { k := pixel.K(c) cols := pixel.Or("MemoryMap Columns", o.Columns, 4) for _, b := range banks { diff --git a/code/paletteeditor.go b/code/paletteeditor.go index 32e5f35..27cb689 100644 --- a/code/paletteeditor.go +++ b/code/paletteeditor.go @@ -16,7 +16,7 @@ type PaletteEditorOptions struct { } type PaletteEditorResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/code/pixeleditor.go b/code/pixeleditor.go index d570d74..07c6474 100644 --- a/code/pixeleditor.go +++ b/code/pixeleditor.go @@ -27,7 +27,7 @@ type PixelEditorOptions struct { } type PixelEditorResult struct { - Element *ui.Element + Element ui.Element // Changed is the art; Copied is COPY. Changed, Copied bool } @@ -129,7 +129,7 @@ func PixelEditor(c *ui.Context, text string, art *string, o PixelEditorOptions) } // canvas paints cells, checkers where clear, grid and cursor. -func canvas(shell *ui.Element, k pixel.Kit, st pixel.State, cells []int, w, z, cur int, grid bool) { +func canvas(shell ui.Element, k pixel.Kit, st pixel.State, cells []int, w, z, cur int, grid bool) { shell.Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) for i, v := range cells { diff --git a/code/profilerbars.go b/code/profilerbars.go index 13f87e9..6e92b7b 100644 --- a/code/profilerbars.go +++ b/code/profilerbars.go @@ -25,7 +25,7 @@ type ProfilerBarsOptions struct { var depths = []pixel.Slot{pixel.Accent, pixel.Info, pixel.Good, pixel.Warn, pixel.Magic} // ProfilerBars stacks spans under their parents, each bar its time. -func ProfilerBars(c *ui.Context, text string, root Span, o ProfilerBarsOptions) *ui.Element { +func ProfilerBars(c *ui.Context, text string, root Span, o ProfilerBarsOptions) ui.Element { k := pixel.K(c) w := pixel.Or("ProfilerBars Width", o.Width, 240) if root.Length <= 0 { diff --git a/code/registerview.go b/code/registerview.go index 01700ce..b8fbc72 100644 --- a/code/registerview.go +++ b/code/registerview.go @@ -27,7 +27,7 @@ type seen struct { const flags = "NV-BDIZC" // RegisterView shows registers and flags; the last change lights. -func RegisterView(c *ui.Context, text string, cpu CPU, o RegisterViewOptions) *ui.Element { +func RegisterView(c *ui.Context, text string, cpu CPU, o RegisterViewOptions) ui.Element { k := pixel.K(c) col := ui.Column(c).Gap(k.D(1)).Role(ui.RoleGroup).Label(text) sn := ui.Local(col, regKey{}, func() seen { return seen{cpu: cpu} }) diff --git a/code/shortcutlist.go b/code/shortcutlist.go index 3c879cb..ce1040f 100644 --- a/code/shortcutlist.go +++ b/code/shortcutlist.go @@ -22,7 +22,7 @@ type ShortcutListOptions struct { } // ShortcutList pairs actions with their keycaps. -func ShortcutList(c *ui.Context, label string, items []Shortcut, o ShortcutListOptions) *ui.Element { +func ShortcutList(c *ui.Context, label string, items []Shortcut, o ShortcutListOptions) ui.Element { k := pixel.K(c) for _, it := range items { if len(it.Keys) == 0 { diff --git a/code/spritesheet.go b/code/spritesheet.go index 68d941e..88d96d7 100644 --- a/code/spritesheet.go +++ b/code/spritesheet.go @@ -23,7 +23,7 @@ type SpriteSheetOptions struct { } type SpriteSheetResult struct { - Element *ui.Element + Element ui.Element // Changed is a frame chosen or play pressed. Changed bool } diff --git a/code/terminal.go b/code/terminal.go index d6d23db..69e18ef 100644 --- a/code/terminal.go +++ b/code/terminal.go @@ -19,7 +19,7 @@ type TerminalOptions struct { } type TerminalResult struct { - Element *ui.Element + Element ui.Element // Ran is the command Enter sent, else "". Ran string Changed bool diff --git a/data/accordion.go b/data/accordion.go index d782fc6..91d6a13 100644 --- a/data/accordion.go +++ b/data/accordion.go @@ -16,7 +16,7 @@ type AccordionOptions struct { } type AccordionResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/bestiary.go b/data/bestiary.go index c4eaee5..a9a498c 100644 --- a/data/bestiary.go +++ b/data/bestiary.go @@ -26,7 +26,7 @@ type BestiaryOptions struct { } // Bestiary is a monster card; unseen ones are silhouettes. -func Bestiary(c *ui.Context, m Monster, o BestiaryOptions) *ui.Element { +func Bestiary(c *ui.Context, m Monster, o BestiaryOptions) ui.Element { k := pixel.K(c) sc := pixel.Or("Bestiary Scale", o.Scale, 2) if m.Sprite == nil || sc > 4 { diff --git a/data/cardgrid.go b/data/cardgrid.go index fc6a66f..6d59124 100644 --- a/data/cardgrid.go +++ b/data/cardgrid.go @@ -13,7 +13,7 @@ type CardGridOptions struct { } // CardGrid fits as many card columns as width allows. -func CardGrid(c *ui.Context, n int, o CardGridOptions, card func(i int)) *ui.Element { +func CardGrid(c *ui.Context, n int, o CardGridOptions, card func(i int)) ui.Element { mw := pixel.Or("CardGrid MinWidth", o.MinWidth, 96) box := ui.Box(c).FillWidth() w, check := pixel.WrapWidth(c, box) diff --git a/data/carousel.go b/data/carousel.go index 9b09096..8d4dca3 100644 --- a/data/carousel.go +++ b/data/carousel.go @@ -19,7 +19,7 @@ type CarouselOptions struct { } type CarouselResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/collapsible.go b/data/collapsible.go index 94987a1..4ac1b2e 100644 --- a/data/collapsible.go +++ b/data/collapsible.go @@ -13,7 +13,7 @@ type CollapsibleOptions struct { } type CollapsibleResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -25,7 +25,7 @@ func Collapsible(c *ui.Context, title string, open *bool, o CollapsibleOptions, col.Children(func() { // The base turns before it learns it is off. v := *open - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.CollapsibleBase(c, &v).Trigger.Label(title).Disabled(o.Disabled) }) if res.Changed = v != *open && !base.IsDisabled(); res.Changed { diff --git a/data/data.go b/data/data.go index ec050fb..b33c253 100644 --- a/data/data.go +++ b/data/data.go @@ -28,7 +28,7 @@ var ( ) // well sinks box and finishes it as st says. -func well(c *ui.Context, box *ui.Element, st pixel.State) { +func well(c *ui.Context, box ui.Element, st pixel.State) { k := pixel.K(c) box.Padding(k.D(2)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/data/descriptionlist.go b/data/descriptionlist.go index ded82b6..31a64ce 100644 --- a/data/descriptionlist.go +++ b/data/descriptionlist.go @@ -13,7 +13,7 @@ type DescriptionListOptions struct { } // DescriptionList sets terms beside their descriptions. -func DescriptionList(c *ui.Context, pairs [][2]string, o DescriptionListOptions) *ui.Element { +func DescriptionList(c *ui.Context, pairs [][2]string, o DescriptionListOptions) ui.Element { k := pixel.K(c) tw := pixel.Or("DescriptionList TermWidth", o.TermWidth, 80) return ui.Column(c).FillWidth().Gap(k.D(4)).Children(func() { diff --git a/data/feed.go b/data/feed.go index b3cce57..c85bc31 100644 --- a/data/feed.go +++ b/data/feed.go @@ -16,7 +16,7 @@ type Activity struct { type FeedOptions struct{} // Feed lists activity: icon, text and time, dotted apart. -func Feed(c *ui.Context, entries []Activity, o FeedOptions) *ui.Element { +func Feed(c *ui.Context, entries []Activity, o FeedOptions) ui.Element { k := pixel.K(c) return ui.Column(c).FillWidth().Children(func() { for i, a := range entries { diff --git a/data/filterbar.go b/data/filterbar.go index e3e8e72..84e9adf 100644 --- a/data/filterbar.go +++ b/data/filterbar.go @@ -16,7 +16,7 @@ type FilterBarOptions struct { } type FilterBarResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/gridview.go b/data/gridview.go index 65a5d06..17f14fe 100644 --- a/data/gridview.go +++ b/data/gridview.go @@ -18,7 +18,7 @@ type GridViewOptions struct { } type GridViewResult struct { - Element *ui.Element + Element ui.Element // Submitted is Enter or a double click on an item. Changed, Submitted bool } diff --git a/data/infinitelist.go b/data/infinitelist.go index 3055230..6456551 100644 --- a/data/infinitelist.go +++ b/data/infinitelist.go @@ -17,7 +17,7 @@ type InfiniteListOptions struct { } type InfiniteListResult struct { - Element *ui.Element + Element ui.Element // More asks for rows past the n there are. More bool } @@ -42,7 +42,7 @@ func InfiniteList(c *ui.Context, text string, n int, o InfiniteListOptions, row if o.Done { rows = n } - var e *ui.Element + var e ui.Element box.Children(func() { e = ui.List(c, &s.list, rows, pixel.Lazy(c, func(i int) { if i < n { diff --git a/data/inventory.go b/data/inventory.go index 02ea471..6ff0e4e 100644 --- a/data/inventory.go +++ b/data/inventory.go @@ -16,7 +16,7 @@ type InventoryOptions struct { } type InventoryResult struct { - Element *ui.Element + Element ui.Element // Swapped reports two slots traded by a drag. Changed, Swapped bool } diff --git a/data/itemslot.go b/data/itemslot.go index e0679e8..3ca0106 100644 --- a/data/itemslot.go +++ b/data/itemslot.go @@ -26,7 +26,7 @@ type ItemSlotOptions struct { } // ItemSlot shows an item, count, rarity edge and cooldown. -func ItemSlot(c *ui.Context, it Item, o ItemSlotOptions) *ui.Element { +func ItemSlot(c *ui.Context, it Item, o ItemSlotOptions) ui.Element { k := pixel.K(c) o.check() return ui.Box(c).Role(ui.RoleImage).Label(it.String()).Size(k.D(SlotSize), k.D(SlotSize)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/data/leaderboard.go b/data/leaderboard.go index d66f3b0..f67ce41 100644 --- a/data/leaderboard.go +++ b/data/leaderboard.go @@ -23,7 +23,7 @@ type LeaderboardOptions struct { } // Leaderboard ranks scores; the top three wear medals. -func Leaderboard(c *ui.Context, scores []Score, o LeaderboardOptions) *ui.Element { +func Leaderboard(c *ui.Context, scores []Score, o LeaderboardOptions) ui.Element { k := pixel.K(c) if o.Player < -1 || o.Player >= len(scores) { panic(fmt.Sprintf("data: Leaderboard Player %d of %d", o.Player, len(scores))) diff --git a/data/list.go b/data/list.go index a6752c0..a914de6 100644 --- a/data/list.go +++ b/data/list.go @@ -15,7 +15,7 @@ type ListOptions struct { } type ListResult struct { - Element *ui.Element + Element ui.Element // Submitted is Enter or a double click on a row. Changed, Submitted bool } @@ -33,7 +33,7 @@ func List(c *ui.Context, text string, n int, selected *int, o ListOptions, name if off { st.Selected = nil } - var e *ui.Element + var e ui.Element box.Children(func() { e = ui.List(c, st, n, pixel.Lazy(c, func(i int) { on := i == *selected diff --git a/data/matrix.go b/data/matrix.go index a15720e..0d5d866 100644 --- a/data/matrix.go +++ b/data/matrix.go @@ -14,7 +14,7 @@ type MatrixOptions struct { } type MatrixResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/outline.go b/data/outline.go index 05f0833..151828b 100644 --- a/data/outline.go +++ b/data/outline.go @@ -15,7 +15,7 @@ type OutlineOptions struct { } type OutlineResult struct { - Element *ui.Element + Element ui.Element Changed, Submitted bool } @@ -47,7 +47,7 @@ func Outline[K comparable](c *ui.Context, text string, roots []K, children func( if off { st.list.Selected = nil } - var e *ui.Element + var e ui.Element box.Children(func() { e = ui.List(c, &st.list, len(rows), pixel.Lazy(c, func(i int) { r, on, ink := rows[i], i == st.at, pixel.Ink diff --git a/data/pager.go b/data/pager.go index ee8eb8d..a811d2b 100644 --- a/data/pager.go +++ b/data/pager.go @@ -20,7 +20,7 @@ type PagerOptions struct { } type PagerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/propertygrid.go b/data/propertygrid.go index f22d0e3..bcea39b 100644 --- a/data/propertygrid.go +++ b/data/propertygrid.go @@ -27,7 +27,7 @@ type PropertyGridOptions struct { } type PropertyGridResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/sortable.go b/data/sortable.go index feffa83..1f14f51 100644 --- a/data/sortable.go +++ b/data/sortable.go @@ -16,7 +16,7 @@ type SortableOptions struct { } type SortableResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -33,7 +33,7 @@ func Sortable(c *ui.Context, text string, items *[]string, o SortableOptions) So k := pixel.K(c) n := len(*items) off := o.Disabled || pixel.Current(c).Inert - 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.RoleList).Label(text).Disabled(o.Disabled) }) s := ui.Local(base, sortKey{}, func() sorting { return sorting{} }) diff --git a/data/statcompare.go b/data/statcompare.go index 038b0a3..a5f3414 100644 --- a/data/statcompare.go +++ b/data/statcompare.go @@ -18,7 +18,7 @@ type Change struct { type StatCompareOptions struct{} // StatCompare lists stats old → new with ▲▼ deltas. -func StatCompare(c *ui.Context, changes []Change, o StatCompareOptions) *ui.Element { +func StatCompare(c *ui.Context, changes []Change, o StatCompareOptions) ui.Element { k := pixel.K(c) lw, ow, nw, dw := 0, 0, 0, 0 deltas := make([]string, len(changes)) diff --git a/data/table.go b/data/table.go index 9c64af2..064780b 100644 --- a/data/table.go +++ b/data/table.go @@ -28,7 +28,7 @@ type TableOptions struct { } type TableResult struct { - Element *ui.Element + Element ui.Element // Sorted reports a header click; sort the rows by *sort. Changed, Submitted, Sorted bool } @@ -51,11 +51,11 @@ func Table(c *ui.Context, text string, cols []Column, rows int, sort *Sort, sele st.Selected = nil } res := TableResult{Element: box} - var list *ui.Element + var list ui.Element box.Children(func() { ui.Row(c).FocusGroup(ui.Horizontal).Role(ui.RoleRow).Children(func() { for i, col := range cols { - shell, base, hs := pixel.Shell(c, func() *ui.Element { + shell, base, hs := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Role(ui.RoleColumnHeader).Label(col.Title).Disabled(o.Disabled) }) if base.Clicked() { diff --git a/data/timeline.go b/data/timeline.go index dd4b024..1d494f0 100644 --- a/data/timeline.go +++ b/data/timeline.go @@ -15,7 +15,7 @@ type Event struct { type TimelineOptions struct{} // Timeline strings dated events down a line of markers. -func Timeline(c *ui.Context, events []Event, o TimelineOptions) *ui.Element { +func Timeline(c *ui.Context, events []Event, o TimelineOptions) ui.Element { k := pixel.K(c) return ui.Column(c).FillWidth().Children(func() { for i, ev := range events { diff --git a/data/transfer.go b/data/transfer.go index dc61d2a..94483df 100644 --- a/data/transfer.go +++ b/data/transfer.go @@ -18,7 +18,7 @@ type TransferOptions struct { } type TransferResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/data/tree.go b/data/tree.go index 74a01ec..bc71526 100644 --- a/data/tree.go +++ b/data/tree.go @@ -25,7 +25,7 @@ type TreeOptions struct { } type TreeResult struct { - Element *ui.Element + Element ui.Element Changed, Toggled bool } @@ -55,7 +55,7 @@ func Tree(c *ui.Context, text string, roots []TreeNode, selected *string, o Tree for i := range roots { tops = append(tops, strconv.Itoa(i)) } - 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.RoleTree).Label(text).Disabled(o.Disabled) }) opened := ui.Local(base, treeKey{}, func() map[string]bool { return map[string]bool{} }) diff --git a/datetime/agenda.go b/datetime/agenda.go index 4ee9a90..b8d49f8 100644 --- a/datetime/agenda.go +++ b/datetime/agenda.go @@ -13,7 +13,7 @@ import ( type AgendaOptions struct{} // Agenda groups events by day, earliest first. -func Agenda(c *ui.Context, events []Event, o AgendaOptions) *ui.Element { +func Agenda(c *ui.Context, events []Event, o AgendaOptions) ui.Element { k := pixel.K(c) sorted := slices.Clone(events) slices.SortStableFunc(sorted, func(a, b Event) int { return a.Start.Compare(b.Start) }) diff --git a/datetime/alarm.go b/datetime/alarm.go index 8b44272..7964244 100644 --- a/datetime/alarm.go +++ b/datetime/alarm.go @@ -23,7 +23,7 @@ type AlarmOptions struct { } type AlarmResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/datetime/analogclock.go b/datetime/analogclock.go index 4e57228..3fa1a6e 100644 --- a/datetime/analogclock.go +++ b/datetime/analogclock.go @@ -15,7 +15,7 @@ type AnalogClockOptions struct { } // AnalogClock ticks each second; its hands are Bresenham lines. -func AnalogClock(c *ui.Context, o AnalogClockOptions) *ui.Element { +func AnalogClock(c *ui.Context, o AnalogClockOptions) ui.Element { k := pixel.K(c) size := pixel.Or("AnalogClock Size", o.Size, 48) // Wake on the next whole second. diff --git a/datetime/calendar.go b/datetime/calendar.go index e73e09f..f556e0a 100644 --- a/datetime/calendar.go +++ b/datetime/calendar.go @@ -12,7 +12,7 @@ type CalendarOptions struct { } type CalendarResult struct { - Element *ui.Element + Element ui.Element // Picked is a click, Enter or Space on a day. Changed, Picked bool } diff --git a/datetime/clocks_test.go b/datetime/clocks_test.go index 30efc30..68a496e 100644 --- a/datetime/clocks_test.go +++ b/datetime/clocks_test.go @@ -169,7 +169,7 @@ func TestRelativeTimeAsksForTheNextMinute(t *testing.T) { at := clk.t.Add(-90 * time.Second) tt := view(300, 60, clk, false, func(c *ui.Context) { datetime.RelativeTime(c, at, datetime.RelativeTimeOptions{}) }) pixel.ResetDue() - before := time.Now() + before := tt.Now() tt.Frame() // The words change at two minutes: thirty seconds on. if wait := pixel.Due().Sub(before); wait < 29*time.Second || wait > 31*time.Second { diff --git a/datetime/countdown.go b/datetime/countdown.go index fc74e8e..b45e36e 100644 --- a/datetime/countdown.go +++ b/datetime/countdown.go @@ -12,7 +12,7 @@ import ( type CountdownOptions struct{} type CountdownResult struct { - Element *ui.Element + Element ui.Element // Done is true in the frame time runs out. Done bool } diff --git a/datetime/dateinput.go b/datetime/dateinput.go index 6558236..77e00b9 100644 --- a/datetime/dateinput.go +++ b/datetime/dateinput.go @@ -15,7 +15,7 @@ type DateInputOptions struct { } type DateInputResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -28,7 +28,7 @@ type popping struct{ open, held bool } func DateInput(c *ui.Context, text string, date *time.Time, o DateInputOptions) DateInputResult { k := pixel.K(c) old := *date - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Role(ui.RolePopUpButton).Label(text).Disabled(o.Disabled) }) pop := ui.Local(base, dateKey{}, func() popping { return popping{} }) @@ -53,7 +53,7 @@ func DateInput(c *ui.Context, text string, date *time.Time, o DateInputOptions) g.Sprite(g.W-icon.Calendar.W-4, (g.H-icon.Calendar.H)/2, icon.Calendar, 0, pixel.Faint, 1) g.Finish(st) }) - ui.PopoverBase(c, base, open, func(panel *ui.Element) { + ui.PopoverBase(c, base, open, func(panel ui.Element) { panel.Padding(k.D(5)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) g.Frame(0, 0, g.W, g.H, pixel.Window, pixel.Face) diff --git a/datetime/daterange.go b/datetime/daterange.go index 228290d..dcc39a8 100644 --- a/datetime/daterange.go +++ b/datetime/daterange.go @@ -12,7 +12,7 @@ type DateRangeOptions struct { } type DateRangeResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/datetime/dayschedule.go b/datetime/dayschedule.go index 88b236f..97ade42 100644 --- a/datetime/dayschedule.go +++ b/datetime/dayschedule.go @@ -19,7 +19,7 @@ type DayScheduleOptions struct { const hourH, gutter = pixel.LineHeight + 4, 24 // DaySchedule places events on hour rows by start and length. -func DaySchedule(c *ui.Context, events []Event, o DayScheduleOptions) *ui.Element { +func DaySchedule(c *ui.Context, events []Event, o DayScheduleOptions) ui.Element { k := pixel.K(c) from, to := o.From, o.To if from == 0 && to == 0 { diff --git a/datetime/digitalclock.go b/datetime/digitalclock.go index c5e91b9..f276e94 100644 --- a/datetime/digitalclock.go +++ b/datetime/digitalclock.go @@ -16,7 +16,7 @@ type DigitalClockOptions struct { } // DigitalClock shows HH:MM:SS in seven segments; colons blink. -func DigitalClock(c *ui.Context, o DigitalClockOptions) *ui.Element { +func DigitalClock(c *ui.Context, o DigitalClockOptions) ui.Element { k := pixel.K(c) sc := pixel.Or("DigitalClock Scale", o.Scale, 2) if sc > 4 { diff --git a/datetime/eventchip.go b/datetime/eventchip.go index 03dfb3d..f23fe34 100644 --- a/datetime/eventchip.go +++ b/datetime/eventchip.go @@ -12,7 +12,7 @@ type EventChipOptions struct { } // EventChip is a small chip with time and title. -func EventChip(c *ui.Context, e Event, o EventChipOptions) *ui.Element { +func EventChip(c *ui.Context, e Event, o EventChipOptions) ui.Element { k := pixel.K(c) edge := o.Slot if edge == 0 { diff --git a/datetime/grid.go b/datetime/grid.go index b78859e..ff94c26 100644 --- a/datetime/grid.go +++ b/datetime/grid.go @@ -27,7 +27,7 @@ func addMonths(t time.Time, n int) time.Time { } // month moves *cur by keys; clicks, Enter and Space pick. -func month(c *ui.Context, text string, cur *time.Time, disabled, focus bool, mark func(time.Time) look) (e, grid *ui.Element, picked bool) { +func month(c *ui.Context, text string, cur *time.Time, disabled, focus bool, mark func(time.Time) look) (e, grid ui.Element, picked bool) { k := pixel.K(c) e = ui.Column(c).Gap(k.D(2)).Role(ui.RoleGroup).Label(text) e.Children(func() { @@ -54,12 +54,12 @@ func month(c *ui.Context, text string, cur *time.Time, disabled, focus bool, mar } } }) - 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.RoleTable).Label(text).Disabled(disabled) }) grid = base if focus { - base.AutoFocus() + pixel.AutoFocus(c, base) } y, m, d := cur.Date() for _, mv := range []struct { diff --git a/datetime/monthpicker.go b/datetime/monthpicker.go index 66bc232..0aafb5c 100644 --- a/datetime/monthpicker.go +++ b/datetime/monthpicker.go @@ -17,7 +17,7 @@ type MonthPickerOptions struct { } type MonthPickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/datetime/relativetime.go b/datetime/relativetime.go index 37f034a..64d86dd 100644 --- a/datetime/relativetime.go +++ b/datetime/relativetime.go @@ -15,7 +15,7 @@ type RelativeTimeOptions struct { } // RelativeTime says how long since or until t, each minute. -func RelativeTime(c *ui.Context, t time.Time, o RelativeTimeOptions) *ui.Element { +func RelativeTime(c *ui.Context, t time.Time, o RelativeTimeOptions) ui.Element { k := pixel.K(c) ink := o.Slot if ink == 0 { diff --git a/datetime/seasonwheel.go b/datetime/seasonwheel.go index 548b36f..a49ad16 100644 --- a/datetime/seasonwheel.go +++ b/datetime/seasonwheel.go @@ -15,7 +15,7 @@ type SeasonWheelOptions struct { } type SeasonWheelResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -32,7 +32,7 @@ func SeasonWheel(c *ui.Context, text string, season *pixel.Season, o SeasonWheel panic(fmt.Sprintf("datetime: unknown Season %d", *season)) } old := *season - 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.RoleRadioGroup).Label(text).Disabled(o.Disabled) }) if pixel.Shortcut(base, 0, ui.KeyRight) || pixel.Shortcut(base, 0, ui.KeyDown) { diff --git a/datetime/stopwatch.go b/datetime/stopwatch.go index a26eec4..048a901 100644 --- a/datetime/stopwatch.go +++ b/datetime/stopwatch.go @@ -16,7 +16,7 @@ type StopwatchOptions struct { } type StopwatchResult struct { - Element *ui.Element + Element ui.Element Started, Stopped, Lapped, Reset bool } diff --git a/datetime/sundial.go b/datetime/sundial.go index cadda74..7545c68 100644 --- a/datetime/sundial.go +++ b/datetime/sundial.go @@ -16,7 +16,7 @@ type SunDialOptions struct { } type SunDialResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -30,7 +30,7 @@ func SunDial(c *ui.Context, text string, hour *int, o SunDialOptions) SunDialRes panic(fmt.Sprintf("datetime: SunDial hour %d outside 0–23", *hour)) } old := *hour - 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.RoleSlider).Label(text).Disabled(o.Disabled) }) if pixel.Shortcut(base, 0, ui.KeyLeft) { diff --git a/datetime/timeinput.go b/datetime/timeinput.go index 72a373a..dfca410 100644 --- a/datetime/timeinput.go +++ b/datetime/timeinput.go @@ -15,7 +15,7 @@ type TimeInputOptions struct { } type TimeInputResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -30,7 +30,7 @@ type timing struct { func TimeInput(c *ui.Context, text string, tm *time.Time, o TimeInputOptions) TimeInputResult { k := pixel.K(c) old := *tm - 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.RoleTextField).Label(text).Disabled(o.Disabled) }) s := ui.Local(base, timeKey{}, func() timing { return timing{first: -1} }) diff --git a/datetime/timer.go b/datetime/timer.go index cfe160d..fb62e7b 100644 --- a/datetime/timer.go +++ b/datetime/timer.go @@ -16,7 +16,7 @@ type TimerOptions struct { } type TimerResult struct { - Element *ui.Element + Element ui.Element // Done is true in the frame the time runs out. Done bool } diff --git a/datetime/views_test.go b/datetime/views_test.go index ad20f1d..8061b59 100644 --- a/datetime/views_test.go +++ b/datetime/views_test.go @@ -238,7 +238,7 @@ func TestTodayMarksMoveAtMidnight(t *testing.T) { } { tt := view(600, 400, clk, false, build) pixel.ResetDue() - before := time.Now() + before := tt.Now() tt.Frame() if due := pixel.Due(); due.IsZero() || due.Sub(before) > 2*time.Second { t.Fatalf("%s: no rebuild asked for midnight, due %v", name, due) @@ -264,7 +264,7 @@ func TestRelativeTimeRoundsTheFutureUp(t *testing.T) { at := clk.t.Add(tc.ahead) tt := view(300, 60, clk, false, func(c *ui.Context) { datetime.RelativeTime(c, at, datetime.RelativeTimeOptions{}) }) pixel.ResetDue() - before := time.Now() + before := tt.Now() tt.Frame() if wait := pixel.Due().Sub(before); !shows(tt, tc.words) || wait < tc.wait-time.Second || wait > tc.wait+time.Second { t.Fatalf("%v ahead: %q, next repaint in %v", tc.ahead, tt.Texts(), wait) diff --git a/datetime/weekstrip.go b/datetime/weekstrip.go index 2474e99..4be00e8 100644 --- a/datetime/weekstrip.go +++ b/datetime/weekstrip.go @@ -15,7 +15,7 @@ type WeekStripOptions struct { } type WeekStripResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -26,7 +26,7 @@ const dayW, dayH = 22, 2 * pixel.LineHeight func WeekStrip(c *ui.Context, text string, date *time.Time, o WeekStripOptions) WeekStripResult { k := pixel.K(c) old := *date - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) if pixel.Shortcut(base, 0, ui.KeyLeft) { diff --git a/datetime/yearpicker.go b/datetime/yearpicker.go index 4c8f802..1d312bb 100644 --- a/datetime/yearpicker.go +++ b/datetime/yearpicker.go @@ -15,7 +15,7 @@ type YearPickerOptions struct { } type YearPickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/datetime/yearview.go b/datetime/yearview.go index ba53335..1d5f209 100644 --- a/datetime/yearview.go +++ b/datetime/yearview.go @@ -15,7 +15,7 @@ type YearViewOptions struct{} const miniW, miniH, monthW, monthH = 10, 7, 7*miniW + 4, 6*miniH + pixel.LineHeight + 2 // YearView sets a year's twelve months small; today is marked. -func YearView(c *ui.Context, year int, o YearViewOptions) *ui.Element { +func YearView(c *ui.Context, year int, o YearViewOptions) ui.Element { k := pixel.K(c) now := today(c, k) return ui.Box(c).Size(k.D(4*monthW), k.D(3*monthH)).Role(ui.RoleImage).Label(strconv.Itoa(year)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/desk/appwindow.go b/desk/appwindow.go index 211e99b..688a4a9 100644 --- a/desk/appwindow.go +++ b/desk/appwindow.go @@ -20,7 +20,7 @@ type AppWindowOptions struct { } type AppWindowResult struct { - Element *ui.Element + Element ui.Element // Closed is the close box; Moved, a drag or arrow. Closed, Moved bool } @@ -64,7 +64,7 @@ func AppWindow(c *ui.Context, title string, x, y *int, o AppWindowOptions, conte g.Frame(0, 0, g.W, g.H, pixel.Raised, pixel.Face) }).Children(func() { ui.Row(c).FillWidth().Gap(k.D(2)).AlignItems(ui.Center).Children(func() { - 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.RoleGroup).Label(title).Description(k.Msg("arrows_move")).Disabled(o.Disabled) }) if !base.IsDisabled() { diff --git a/desk/bootscreen.go b/desk/bootscreen.go index 559bf1f..4803fa5 100644 --- a/desk/bootscreen.go +++ b/desk/bootscreen.go @@ -19,7 +19,7 @@ type BootScreenOptions struct { } type BootScreenResult struct { - Element *ui.Element + Element ui.Element // Done is the frame the last line shows. Done bool } diff --git a/desk/calculator.go b/desk/calculator.go index 8cf874b..39c060c 100644 --- a/desk/calculator.go +++ b/desk/calculator.go @@ -16,7 +16,7 @@ type CalculatorOptions struct { } type CalculatorResult struct { - Element *ui.Element + Element ui.Element // Changed is a key pressed this frame. Changed bool } diff --git a/desk/clipboardpanel.go b/desk/clipboardpanel.go index 450c0e3..5ed2fcc 100644 --- a/desk/clipboardpanel.go +++ b/desk/clipboardpanel.go @@ -18,7 +18,7 @@ type ClipboardPanelOptions struct { } type ClipboardPanelResult struct { - Element *ui.Element + Element ui.Element // Copied is the clip copied this frame, else -1. Copied int } @@ -58,7 +58,7 @@ func ClipboardPanel(c *ui.Context, text string, clips []string, o ClipboardPanel for i, clip := range clips { // Keyed by the clip: new ones come in on top. ui.Box(c).Key(clip).FillWidth().Children(func() { - 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.RoleButton).Label(clip).Description(k.Msg("copy")).Disabled(o.Disabled) }) if !base.IsDisabled() && base.Clicked() { diff --git a/desk/controlpanel.go b/desk/controlpanel.go index 7f44412..1a1c4ec 100644 --- a/desk/controlpanel.go +++ b/desk/controlpanel.go @@ -20,7 +20,7 @@ type ControlPanelOptions struct { } type ControlPanelResult struct { - Element *ui.Element + Element ui.Element // Opened is the applet opened this frame, else -1. Opened int } diff --git a/desk/desktop.go b/desk/desktop.go index ff0d146..c11ba24 100644 --- a/desk/desktop.go +++ b/desk/desktop.go @@ -13,7 +13,7 @@ type DesktopOptions struct { } // Desktop lays icons down a dithered wallpaper, column by column. -func Desktop(c *ui.Context, text string, o DesktopOptions, icons func()) *ui.Element { +func Desktop(c *ui.Context, text string, o DesktopOptions, icons func()) ui.Element { k := pixel.K(c) o.Wallpaper.name(k) w, h := pixel.Or("Desktop Width", o.Width, 320), pixel.Or("Desktop Height", o.Height, 200) diff --git a/desk/desktopicon.go b/desk/desktopicon.go index 2e98477..39731af 100644 --- a/desk/desktopicon.go +++ b/desk/desktopicon.go @@ -15,7 +15,7 @@ type DesktopIconOptions struct { } type DesktopIconResult struct { - Element *ui.Element + Element ui.Element // Clicked chooses it; Opened is a double click or Enter. Clicked, Opened bool } @@ -29,7 +29,7 @@ func DesktopIcon(c *ui.Context, label string, icon *pixel.Sprite, o DesktopIconO if label == "" { panic("desk: DesktopIcon has no label") } - 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.RoleButton).Label(label).Disabled(o.Disabled) }) res := DesktopIconResult{Element: shell} diff --git a/desk/diskdefrag.go b/desk/diskdefrag.go index 1e16f2a..08825ab 100644 --- a/desk/diskdefrag.go +++ b/desk/diskdefrag.go @@ -19,7 +19,7 @@ type DiskDefragOptions struct { } type DiskDefragResult struct { - Element *ui.Element + Element ui.Element // Done is the frame the last block falls in place. Done bool } diff --git a/desk/dock.go b/desk/dock.go index 179f33d..549181a 100644 --- a/desk/dock.go +++ b/desk/dock.go @@ -20,7 +20,7 @@ type DockOptions struct { } type DockResult struct { - Element *ui.Element + Element ui.Element // Launched is the app pressed this frame, else -1. Launched int } @@ -68,7 +68,7 @@ func Dock(c *ui.Context, text string, apps []DockApp, o DockOptions) DockResult for i, a := range apps { // Keyed by name: apps come and go. ui.Box(c).Key(a.Name).Children(func() { - 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.RoleButton).Label(a.Name).Disabled(o.Disabled) }) up := st.Hover || st.Focus diff --git a/desk/lockscreen.go b/desk/lockscreen.go index 5eab05f..81195ca 100644 --- a/desk/lockscreen.go +++ b/desk/lockscreen.go @@ -14,7 +14,7 @@ import ( type LockScreenOptions struct{} type LockScreenResult struct { - Element *ui.Element + Element ui.Element // Unlocked is a key or click opening it this frame. Unlocked bool } @@ -40,7 +40,7 @@ func LockScreen(c *ui.Context, text string, open *bool, o LockScreenOptions) Loc g.Text((g.W-pixel.Measure(press))/2, g.H-3*pixel.LineHeight, press, pixel.Accent, 1) } }) - if panel != nil { + if panel.Valid() { panel.Description(clock + ", " + date + ", " + press) } if woke { diff --git a/desk/minesweeper.go b/desk/minesweeper.go index 6330b2d..f7930f1 100644 --- a/desk/minesweeper.go +++ b/desk/minesweeper.go @@ -23,7 +23,7 @@ type MinesweeperOptions struct { } type MinesweeperResult struct { - Element *ui.Element + Element ui.Element // Won or Lost is the game ending this frame. Won, Lost bool // Flagged is a flag set or lifted; Restarted, the face. diff --git a/desk/notepad.go b/desk/notepad.go index 0ac55d8..adf536c 100644 --- a/desk/notepad.go +++ b/desk/notepad.go @@ -18,7 +18,7 @@ type NotepadOptions struct { } type NotepadResult struct { - Element *ui.Element + Element ui.Element // Saved is FILE, SAVE chosen this frame. Changed, Saved bool } diff --git a/desk/rundialog.go b/desk/rundialog.go index 1ac1362..163fe33 100644 --- a/desk/rundialog.go +++ b/desk/rundialog.go @@ -19,7 +19,7 @@ type RunDialogOptions struct { } type RunDialogResult struct { - Element *ui.Element + Element ui.Element // Canceled is CANCEL, the close box or Escape. Changed, Submitted, Canceled bool } diff --git a/desk/screensaver.go b/desk/screensaver.go index 4352a56..bb4919c 100644 --- a/desk/screensaver.go +++ b/desk/screensaver.go @@ -13,7 +13,7 @@ import ( type ScreensaverOptions struct{} type ScreensaverResult struct { - Element *ui.Element + Element ui.Element // Woke is the input that ended it this frame. Woke bool } diff --git a/desk/shutdownscreen.go b/desk/shutdownscreen.go index 4fed155..b8920fa 100644 --- a/desk/shutdownscreen.go +++ b/desk/shutdownscreen.go @@ -14,7 +14,7 @@ type ShutdownScreenOptions struct { } // ShutdownScreen says it is safe to turn off the computer. -func ShutdownScreen(c *ui.Context, o ShutdownScreenOptions) *ui.Element { +func ShutdownScreen(c *ui.Context, o ShutdownScreenOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("ShutdownScreen Width", o.Width, 320), pixel.Or("ShutdownScreen Height", o.Height, 200) s := k.Msg("safe_to_turn_off") diff --git a/desk/snake.go b/desk/snake.go index ac0b6b2..ac1a6a0 100644 --- a/desk/snake.go +++ b/desk/snake.go @@ -20,7 +20,7 @@ type SnakeOptions struct { } type SnakeResult struct { - Element *ui.Element + Element ui.Element // Score is food eaten; Started and Over are this frame's. Score int Started, Over bool @@ -47,7 +47,7 @@ func Snake(c *ui.Context, text string, o SnakeOptions) SnakeResult { if cols < 6 || rows < 4 { panic(fmt.Sprintf("desk: Snake %d×%d is too small", cols, rows)) } - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) s := ui.Local(shell, snakeKey{}, func() pit { return pit{} }) diff --git a/desk/startmenu.go b/desk/startmenu.go index fdb94d8..861748f 100644 --- a/desk/startmenu.go +++ b/desk/startmenu.go @@ -25,7 +25,7 @@ type StartMenuOptions struct { } type StartMenuResult struct { - Element *ui.Element + Element ui.Element // Launched is the program run this frame, as "GAMES/SNAKE". Launched string } @@ -148,9 +148,9 @@ func (m *start) keys(short func(ui.Modifiers, ui.Key) bool, ps []Program) string } // list shows each open level beside its parent row. -func (m *start) list(c *ui.Context, k pixel.Kit, anchor *ui.Element, ps []Program) string { +func (m *start) list(c *ui.Context, k pixel.Kit, anchor ui.Element, ps []Program) string { run := "" - ui.PopoverBase(c, anchor, &m.open, func(panel *ui.Element) { + ui.PopoverBase(c, anchor, &m.open, func(panel ui.Element) { // Only a pointer that moves lights the row under it. px, py, over := panel.PointerPosition() moved := over && [2]float32{px, py} != m.ptr diff --git a/desk/taskbar.go b/desk/taskbar.go index 4ecaba3..d748e35 100644 --- a/desk/taskbar.go +++ b/desk/taskbar.go @@ -21,7 +21,7 @@ type TaskbarOptions struct { } type TaskbarResult struct { - Element *ui.Element + Element ui.Element // Changed is a window button pressed; Launched a program. Changed bool Launched string diff --git a/desk/trayicons.go b/desk/trayicons.go index 7a3cc54..576076f 100644 --- a/desk/trayicons.go +++ b/desk/trayicons.go @@ -21,7 +21,7 @@ type TrayIconsOptions struct { } type TrayIconsResult struct { - Element *ui.Element + Element ui.Element // Clicked is the icon pressed this frame, else -1. Clicked int } @@ -43,7 +43,7 @@ func TrayIcons(c *ui.Context, text string, icons []TrayIcon, o TrayIconsOptions) // Keyed by tip: icons come and go. ui.Box(c).Key(t.Tip).Children(func() { overlay.Tooltip(c, t.Tip, overlay.TooltipOptions{}, func() { - 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.RoleButton).Label(t.Tip).Disabled(o.Disabled) }) if !base.IsDisabled() && base.Clicked() { diff --git a/desk/veil.go b/desk/veil.go index ec8a469..47d2e7f 100644 --- a/desk/veil.go +++ b/desk/veil.go @@ -15,7 +15,7 @@ type wake struct { } // cover fills the window; keys, clicks, long moves end it. -func cover(c *ui.Context, k pixel.Kit, text string, open *bool, moves bool, draw func(g pixel.Canvas)) (anchor, panel *ui.Element, woke bool) { +func cover(c *ui.Context, k pixel.Kit, text string, open *bool, moves bool, draw func(g pixel.Canvas)) (anchor, panel ui.Element, woke bool) { anchor = ui.Box(c).Role(ui.RoleNone) st := ui.Local(anchor, wakeKey{}, func() wake { return wake{} }) was := *open @@ -25,7 +25,7 @@ func cover(c *ui.Context, k pixel.Kit, text string, open *bool, moves bool, draw if !*open { *st = wake{} } - panel = ui.DialogBase(c, open, func(back, panel *ui.Element) { + panel = ui.DialogBase(c, open, func(back, panel ui.Element) { back.Draw(func(p *ui.Painter, r ui.Rect) { draw(k.Canvas(p, r)) }) back.HandleInput(func(ev ui.InputEvent) bool { // Any button and the wheel end it, as keys do. diff --git a/desk/wallpaperpicker.go b/desk/wallpaperpicker.go index 6b55cfa..c0d27c5 100644 --- a/desk/wallpaperpicker.go +++ b/desk/wallpaperpicker.go @@ -13,7 +13,7 @@ type WallpaperPickerOptions struct { } type WallpaperPickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/display/avatar.go b/display/avatar.go index 57cb659..2deee3a 100644 --- a/display/avatar.go +++ b/display/avatar.go @@ -90,7 +90,7 @@ type AvatarOptions struct { } // Avatar draws a face seeded by name, or a picture. -func Avatar(c *ui.Context, name string, o AvatarOptions) *ui.Element { +func Avatar(c *ui.Context, name string, o AvatarOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) if o.Picture != nil { return Picture(c, o.Picture, PictureOptions{W: 16 * sc, H: 16 * sc, Label: name}) diff --git a/display/avatargroup.go b/display/avatargroup.go index 4b71144..c3cd6de 100644 --- a/display/avatargroup.go +++ b/display/avatargroup.go @@ -15,7 +15,7 @@ type AvatarGroupOptions struct { } // AvatarGroup overlaps faces and counts the rest in a chip. -func AvatarGroup(c *ui.Context, names []string, o AvatarGroupOptions) *ui.Element { +func AvatarGroup(c *ui.Context, names []string, o AvatarGroupOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) shown := names[:min(len(names), pixel.Or("AvatarGroup Max", o.Max, 4))] rest := len(names) - len(shown) diff --git a/display/badge.go b/display/badge.go index 97d3031..fa74fb6 100644 --- a/display/badge.go +++ b/display/badge.go @@ -14,7 +14,7 @@ type BadgeOptions struct { } // Badge shows n, capped at 99+, or text. -func Badge(c *ui.Context, n int, o BadgeOptions) *ui.Element { +func Badge(c *ui.Context, n int, o BadgeOptions) ui.Element { k := pixel.K(c) s := o.Text if s == "" { diff --git a/display/bubble.go b/display/bubble.go index b9793d8..a12c201 100644 --- a/display/bubble.go +++ b/display/bubble.go @@ -29,7 +29,7 @@ type BubbleOptions struct { const tailLen = 5 // Bubble is a speech bubble with a tail around children. -func Bubble(c *ui.Context, o BubbleOptions, children func()) *ui.Element { +func Bubble(c *ui.Context, o BubbleOptions, children func()) ui.Element { if o.Tail > Right { panic(fmt.Sprintf("display: unknown Side %d", o.Tail)) } diff --git a/display/display.go b/display/display.go index 186f321..c7d7db1 100644 --- a/display/display.go +++ b/display/display.go @@ -10,7 +10,7 @@ import ( ) // art is a labeled w×h dot image that fn draws. -func art(c *ui.Context, k pixel.Kit, label string, w, h int, fn func(g pixel.Canvas)) *ui.Element { +func art(c *ui.Context, k pixel.Kit, label string, w, h int, fn func(g pixel.Canvas)) ui.Element { e := ui.Box(c).Size(k.D(w), k.D(h)) if label != "" { e.Label(label).Role(ui.RoleImage) diff --git a/display/emoji.go b/display/emoji.go index 56cc3ed..6b9557f 100644 --- a/display/emoji.go +++ b/display/emoji.go @@ -71,7 +71,7 @@ type EmojiOptions struct { } // Emoji draws one of sixteen hand-placed faces by name. -func Emoji(c *ui.Context, name string, o EmojiOptions) *ui.Element { +func Emoji(c *ui.Context, name string, o EmojiOptions) ui.Element { return Icon(c, EmojiSprite(name), IconOptions{Scale: o.Scale, Label: name}) } diff --git a/display/figure.go b/display/figure.go index 9674119..261b136 100644 --- a/display/figure.go +++ b/display/figure.go @@ -12,7 +12,7 @@ type FigureOptions struct { } // Figure shows content over a centered caption. -func Figure(c *ui.Context, caption string, o FigureOptions, content func()) *ui.Element { +func Figure(c *ui.Context, caption string, o FigureOptions, content func()) ui.Element { k := pixel.K(c) return ui.Column(c).Gap(k.D(3)).AlignItems(ui.Center).Role(ui.RoleGroup).Label(caption).Children(func() { content() diff --git a/display/flag.go b/display/flag.go index a43d8c7..1d7d7b2 100644 --- a/display/flag.go +++ b/display/flag.go @@ -14,7 +14,7 @@ type FlagOptions struct { } // Flag waves on its pole, stepped at 6 fps. -func Flag(c *ui.Context, o FlagOptions) *ui.Element { +func Flag(c *ui.Context, o FlagOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) cloth := slotOr(o.Slot, pixel.Bad) return art(c, k, o.Label, 20*sc, 26*sc, func(g pixel.Canvas) { diff --git a/display/gem.go b/display/gem.go index d6f2c1d..3ac24e2 100644 --- a/display/gem.go +++ b/display/gem.go @@ -63,7 +63,7 @@ type GemOptions struct { } // Gem is a cut gem by rarity, sparkling in steps. -func Gem(c *ui.Context, r Rarity, o GemOptions) *ui.Element { +func Gem(c *ui.Context, r Rarity, o GemOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) ink := r.Slot() return art(c, k, r.name(k), (gem.W+3)*sc, (gem.H+2)*sc, func(g pixel.Canvas) { diff --git a/display/icon.go b/display/icon.go index bbe9d8f..253089b 100644 --- a/display/icon.go +++ b/display/icon.go @@ -14,7 +14,7 @@ type IconOptions struct { } // Icon draws a sprite at ×1–×4 in an ink slot. -func Icon(c *ui.Context, sp *pixel.Sprite, o IconOptions) *ui.Element { +func Icon(c *ui.Context, sp *pixel.Sprite, o IconOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) return art(c, k, o.Label, sp.W*sc, sp.H*sc, func(g pixel.Canvas) { g.Sprite(0, 0, sp, 0, o.Slot, sc) }) } diff --git a/display/keyvalue.go b/display/keyvalue.go index b6240a2..2c9269f 100644 --- a/display/keyvalue.go +++ b/display/keyvalue.go @@ -12,7 +12,7 @@ type KeyValueOptions struct { } // KeyValue joins key and value with a dotted leader. -func KeyValue(c *ui.Context, key, value string, o KeyValueOptions) *ui.Element { +func KeyValue(c *ui.Context, key, value string, o KeyValueOptions) ui.Element { k := pixel.K(c) kw, vw := pixel.Measure(key), pixel.Measure(value) return ui.Box(c).FillWidth().MinWidth(k.D(kw + vw + 8)).Height(k.D(pixel.LineHeight)).Role(ui.RoleText).Label(key + " " + value).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/display/medal.go b/display/medal.go index 6619b30..eb754b8 100644 --- a/display/medal.go +++ b/display/medal.go @@ -30,7 +30,7 @@ type MedalOptions struct { } // Medal is a ribboned medal showing its rank. -func Medal(c *ui.Context, r Rank, o MedalOptions) *ui.Element { +func Medal(c *ui.Context, r Rank, o MedalOptions) ui.Element { if r > Bronze { panic(fmt.Sprintf("display: unknown Rank %d", r)) } diff --git a/display/picture.go b/display/picture.go index c913e3e..bb00b49 100644 --- a/display/picture.go +++ b/display/picture.go @@ -24,7 +24,7 @@ type pictureCache struct { } // Picture reduces img to the palette with Bayer dither. -func Picture(c *ui.Context, img image.Image, o PictureOptions) *ui.Element { +func Picture(c *ui.Context, img image.Image, o PictureOptions) ui.Element { k := pixel.K(c) b := img.Bounds() w, h := pixel.Or("Picture W", o.W, b.Dx()), pixel.Or("Picture H", o.H, b.Dy()) diff --git a/display/placeholder.go b/display/placeholder.go index 9b584ac..ac8f7e3 100644 --- a/display/placeholder.go +++ b/display/placeholder.go @@ -14,7 +14,7 @@ type PlaceholderOptions struct { } // Placeholder is a dithered box with a cross and size. -func Placeholder(c *ui.Context, o PlaceholderOptions) *ui.Element { +func Placeholder(c *ui.Context, o PlaceholderOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("Placeholder W", o.W, 64), pixel.Or("Placeholder H", o.H, 48) size := fmt.Sprintf("%d×%d", w, h) diff --git a/display/portrait.go b/display/portrait.go index dc15004..f4ddc68 100644 --- a/display/portrait.go +++ b/display/portrait.go @@ -12,7 +12,7 @@ type PortraitOptions struct { } // Portrait frames a character's picture over a nameplate. -func Portrait(c *ui.Context, name string, o PortraitOptions, picture func()) *ui.Element { +func Portrait(c *ui.Context, name string, o PortraitOptions, picture func()) ui.Element { k := pixel.K(c) size := pixel.Or("Portrait Size", o.Size, 48) return ui.Column(c).Width(k.D(size + 6)).Role(ui.RoleGroup).Label(name).Children(func() { diff --git a/display/qrcode.go b/display/qrcode.go index 1d0d4e7..504045d 100644 --- a/display/qrcode.go +++ b/display/qrcode.go @@ -22,7 +22,7 @@ type qrCache struct { } // QRCode encodes s at level M, with a quiet zone. -func QRCode(c *ui.Context, s string, o QRCodeOptions) *ui.Element { +func QRCode(c *ui.Context, s string, o QRCodeOptions) ui.Element { k := pixel.K(c) sc := pixel.Or("QRCode Scale", o.Scale, 2) e := ui.Box(c) diff --git a/display/rarityframe.go b/display/rarityframe.go index 69d1a66..6aa72df 100644 --- a/display/rarityframe.go +++ b/display/rarityframe.go @@ -12,7 +12,7 @@ type RarityFrameOptions struct { } // RarityFrame borders children by rarity; legendary carries a glint. -func RarityFrame(c *ui.Context, r Rarity, o RarityFrameOptions, children func()) *ui.Element { +func RarityFrame(c *ui.Context, r Rarity, o RarityFrameOptions, children func()) ui.Element { k := pixel.K(c) ink := r.Slot() pad := 3 + pixel.Or("RarityFrame Padding", o.Padding, 4) diff --git a/display/ribbon.go b/display/ribbon.go index 755e54f..39e8916 100644 --- a/display/ribbon.go +++ b/display/ribbon.go @@ -14,7 +14,7 @@ type RibbonOptions struct { const tail = 12 // Ribbon is a title banner with notched, folded tails. -func Ribbon(c *ui.Context, title string, o RibbonOptions) *ui.Element { +func Ribbon(c *ui.Context, title string, o RibbonOptions) ui.Element { k := pixel.K(c) band := slotOr(o.Slot, pixel.Accent) tw := pixel.Measure(title) diff --git a/display/signpost.go b/display/signpost.go index 850d88b..d1776dd 100644 --- a/display/signpost.go +++ b/display/signpost.go @@ -14,7 +14,7 @@ type SignpostOptions struct { } type SignpostResult struct { - Element *ui.Element + Element ui.Element // Clicked is the board clicked this frame, or -1. Clicked int } @@ -51,7 +51,7 @@ func Signpost(c *ui.Context, boards []string, o SignpostOptions) SignpostResult // board is one wooden arrow, pointing left if asked. func board(c *ui.Context, k pixel.Kit, label string, w int, left, disabled bool) bool { - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(label).Disabled(disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(label).Disabled(disabled) }) h := pixel.LineHeight + 2 shell.Size(k.D(w), k.D(h)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/display/sprite.go b/display/sprite.go index be3bf2c..89ee644 100644 --- a/display/sprite.go +++ b/display/sprite.go @@ -16,7 +16,7 @@ type SpriteOptions struct { } // Sprite plays a multi-slot sprite, mirrored when asked. -func Sprite(c *ui.Context, sp *pixel.Sprite, o SpriteOptions) *ui.Element { +func Sprite(c *ui.Context, sp *pixel.Sprite, o SpriteOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) fps := pixel.Or("Sprite FPS", o.FPS, 0) if o.Mirror { diff --git a/display/stamp.go b/display/stamp.go index ca38866..e6abd0a 100644 --- a/display/stamp.go +++ b/display/stamp.go @@ -13,7 +13,7 @@ type StampOptions struct { } // Stamp is a rubber-stamp mark whose border wears unevenly. -func Stamp(c *ui.Context, s string, o StampOptions) *ui.Element { +func Stamp(c *ui.Context, s string, o StampOptions) ui.Element { k := pixel.K(c) ink := slotOr(o.Slot, pixel.Bad) w, h := pixel.Measure(s)+12, pixel.LineHeight+8 diff --git a/display/stat.go b/display/stat.go index 0b7de99..73a4323 100644 --- a/display/stat.go +++ b/display/stat.go @@ -27,7 +27,7 @@ type StatOptions struct { } // Stat is a big value with a label and delta. -func Stat(c *ui.Context, label, value string, o StatOptions) *ui.Element { +func Stat(c *ui.Context, label, value string, o StatOptions) ui.Element { k := pixel.K(c) return ui.Column(c).Gap(k.D(1)).Role(ui.RoleGroup).Label(label).Children(func() { pixel.Text(c, label, pixel.TextOptions{Slot: pixel.Faint}) diff --git a/display/statusdot.go b/display/statusdot.go index f777481..f89e4f2 100644 --- a/display/statusdot.go +++ b/display/statusdot.go @@ -29,7 +29,7 @@ type StatusDotOptions struct { } // StatusDot is a 7-dot status light. -func StatusDot(c *ui.Context, s Status, o StatusDotOptions) *ui.Element { +func StatusDot(c *ui.Context, s Status, o StatusDotOptions) ui.Element { if int(s) >= len(statusSlots) { panic(fmt.Sprintf("display: unknown Status %d", s)) } diff --git a/display/swatch.go b/display/swatch.go index 193059a..77fd84c 100644 --- a/display/swatch.go +++ b/display/swatch.go @@ -12,7 +12,7 @@ type SwatchOptions struct { } // Swatch shows the active palette's sixteen slots with names. -func Swatch(c *ui.Context, o SwatchOptions) *ui.Element { +func Swatch(c *ui.Context, o SwatchOptions) ui.Element { k := pixel.K(c) cols := pixel.Or("Swatch Columns", o.Columns, 4) cell := 14 + pixel.Measure("AccentLit") diff --git a/display/tag.go b/display/tag.go index be1469f..e5ea35e 100644 --- a/display/tag.go +++ b/display/tag.go @@ -25,7 +25,7 @@ type TagOptions struct { } type TagResult struct { - Element *ui.Element + Element ui.Element Removed bool } @@ -61,7 +61,7 @@ func Tag(c *ui.Context, label string, o TagOptions) TagResult { // removeBox is the × that removes a tag. func removeBox(c *ui.Context, k pixel.Kit, name string, disabled bool) bool { - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(name).Disabled(disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(name).Disabled(disabled) }) shell.Size(k.D(9), k.D(9)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) if st.Hover || st.Press { diff --git a/display/trophy.go b/display/trophy.go index 404f824..75bf291 100644 --- a/display/trophy.go +++ b/display/trophy.go @@ -29,7 +29,7 @@ type TrophyOptions struct { } // Trophy is a gold cup over an engraved plaque. -func Trophy(c *ui.Context, plaque string, o TrophyOptions) *ui.Element { +func Trophy(c *ui.Context, plaque string, o TrophyOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) up := strings.ToUpper(plaque) micro := pixel.MicroOK(up) diff --git a/feedback/achievement.go b/feedback/achievement.go index fd847f9..18a8d0c 100644 --- a/feedback/achievement.go +++ b/feedback/achievement.go @@ -15,7 +15,7 @@ type AchievementOptions struct { } // Achievement slides a trophy card in, holds it, then out. -func Achievement(c *ui.Context, title string, o AchievementOptions) *ui.Element { +func Achievement(c *ui.Context, title string, o AchievementOptions) ui.Element { k := pixel.K(c) w := pixel.Or("Achievement Width", o.Width, 200) e := ui.Box(c) diff --git a/feedback/alert.go b/feedback/alert.go index 3cc4d8f..0f0a221 100644 --- a/feedback/alert.go +++ b/feedback/alert.go @@ -46,7 +46,7 @@ type AlertOptions struct { } type AlertResult struct { - Element *ui.Element + Element ui.Element Closed bool } diff --git a/feedback/banner.go b/feedback/banner.go index 017ea71..a807af9 100644 --- a/feedback/banner.go +++ b/feedback/banner.go @@ -22,7 +22,7 @@ type BannerOptions struct { } type BannerResult struct { - Element *ui.Element + Element ui.Element Action string Dismissed bool } diff --git a/feedback/barsloader.go b/feedback/barsloader.go index 46f003f..4881abe 100644 --- a/feedback/barsloader.go +++ b/feedback/barsloader.go @@ -12,7 +12,7 @@ type BarsLoaderOptions struct{} var heights = [8]int{3, 6, 9, 12, 9, 6, 3, 3} // BarsLoader raises and drops five bars. -func BarsLoader(c *ui.Context, text string, o BarsLoaderOptions) *ui.Element { +func BarsLoader(c *ui.Context, text string, o BarsLoaderOptions) ui.Element { k := pixel.K(c) return ui.Box(c).Role(ui.RoleProgress).Label(text).Size(k.D(23), k.D(12)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/feedback/busyoverlay.go b/feedback/busyoverlay.go index b30e792..298480a 100644 --- a/feedback/busyoverlay.go +++ b/feedback/busyoverlay.go @@ -11,7 +11,7 @@ type BusyOverlayOptions struct{} type busyKey struct{} // BusyOverlay veils busy content, whose controls then take no input. -func BusyOverlay(c *ui.Context, busy bool, o BusyOverlayOptions, content func()) *ui.Element { +func BusyOverlay(c *ui.Context, busy bool, o BusyOverlayOptions, content func()) ui.Element { k := pixel.K(c) outer := ui.Box(c) // held is whether the veil had the focus last frame. @@ -29,7 +29,7 @@ func BusyOverlay(c *ui.Context, busy bool, o BusyOverlayOptions, content func()) s := pixel.Current(c) s.Inert = s.Inert || busy before := c.Root().Focused() - var body *ui.Element + var body ui.Element pixel.Scope(c, s, func() { body = ui.Box(c).Children(content) }) if !busy { return diff --git a/feedback/combocounter.go b/feedback/combocounter.go index 04c3820..0f04a0b 100644 --- a/feedback/combocounter.go +++ b/feedback/combocounter.go @@ -13,7 +13,7 @@ type ComboCounterOptions struct{} type comboKey struct{} // ComboCounter shows ×n COMBO!, popping bigger as n rises. -func ComboCounter(c *ui.Context, count int, o ComboCounterOptions) *ui.Element { +func ComboCounter(c *ui.Context, count int, o ComboCounterOptions) ui.Element { k := pixel.K(c) e := ui.Box(c) s := ui.Local(e, comboKey{}, func() rise { return rise{level: count} }) diff --git a/feedback/continueprompt.go b/feedback/continueprompt.go index 6b96276..2fb8193 100644 --- a/feedback/continueprompt.go +++ b/feedback/continueprompt.go @@ -19,7 +19,7 @@ type ContinuePromptOptions struct { } type ContinuePromptResult struct { - Element *ui.Element + Element ui.Element Yes, No, TimedOut bool } diff --git a/feedback/countup.go b/feedback/countup.go index 3f54cfa..64aa086 100644 --- a/feedback/countup.go +++ b/feedback/countup.go @@ -24,7 +24,7 @@ type counting struct { } // CountUp counts to a new value in eight steps. -func CountUp(c *ui.Context, value int, o CountUpOptions) *ui.Element { +func CountUp(c *ui.Context, value int, o CountUpOptions) ui.Element { k := pixel.K(c) sc := pixel.Or("CountUp Scale", o.Scale, 1) e := ui.Box(c).Role(ui.RoleText).Label(strconv.Itoa(value)) diff --git a/feedback/dotsloader.go b/feedback/dotsloader.go index 9eec262..1e57220 100644 --- a/feedback/dotsloader.go +++ b/feedback/dotsloader.go @@ -9,7 +9,7 @@ import ( type DotsLoaderOptions struct{} // DotsLoader bounces three dots in turn. -func DotsLoader(c *ui.Context, text string, o DotsLoaderOptions) *ui.Element { +func DotsLoader(c *ui.Context, text string, o DotsLoaderOptions) ui.Element { k := pixel.K(c) return ui.Box(c).Role(ui.RoleProgress).Label(text).Size(k.D(18), k.D(8)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/feedback/emptystate.go b/feedback/emptystate.go index 6aa422d..31ce11c 100644 --- a/feedback/emptystate.go +++ b/feedback/emptystate.go @@ -31,7 +31,7 @@ type EmptyStateOptions struct { } type EmptyStateResult struct { - Element *ui.Element + Element ui.Element Acted bool } diff --git a/feedback/errorstate.go b/feedback/errorstate.go index a71aa46..af4ddeb 100644 --- a/feedback/errorstate.go +++ b/feedback/errorstate.go @@ -19,7 +19,7 @@ type ErrorStateOptions struct { } type ErrorStateResult struct { - Element *ui.Element + Element ui.Element Continue bool } diff --git a/feedback/hourglass.go b/feedback/hourglass.go index aef6461..894b8f6 100644 --- a/feedback/hourglass.go +++ b/feedback/hourglass.go @@ -29,7 +29,7 @@ var glass = []string{ } // Hourglass drains in steps, then flips. -func Hourglass(c *ui.Context, text string, o HourglassOptions) *ui.Element { +func Hourglass(c *ui.Context, text string, o HourglassOptions) ui.Element { k := pixel.K(c) return ui.Box(c).Role(ui.RoleProgress).Label(text).Size(k.D(14), k.D(14)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/feedback/inlinemessage.go b/feedback/inlinemessage.go index c97e19e..8d4e81a 100644 --- a/feedback/inlinemessage.go +++ b/feedback/inlinemessage.go @@ -32,7 +32,7 @@ type InlineMessageOptions struct { } // InlineMessage is a small ✓ or ! field note. -func InlineMessage(c *ui.Context, message string, o InlineMessageOptions) *ui.Element { +func InlineMessage(c *ui.Context, message string, o InlineMessageOptions) ui.Element { k := pixel.K(c) slot, _ := o.Kind.look() sp := bang diff --git a/feedback/levelup.go b/feedback/levelup.go index c0660af..95e03da 100644 --- a/feedback/levelup.go +++ b/feedback/levelup.go @@ -21,7 +21,7 @@ type rise struct { } // LevelUp slides in LEVEL UP! with sparkles when level rises. -func LevelUp(c *ui.Context, level int, o LevelUpOptions) *ui.Element { +func LevelUp(c *ui.Context, level int, o LevelUpOptions) ui.Element { k := pixel.K(c) e := ui.Box(c) s := ui.Local(e, levelKey{}, func() rise { return rise{level: level} }) diff --git a/feedback/loadingscreen.go b/feedback/loadingscreen.go index 75a0073..e4fb8d1 100644 --- a/feedback/loadingscreen.go +++ b/feedback/loadingscreen.go @@ -16,7 +16,7 @@ type LoadingScreenOptions struct { } // LoadingScreen shows NOW LOADING, stepping dots and a tip. -func LoadingScreen(c *ui.Context, o LoadingScreenOptions) *ui.Element { +func LoadingScreen(c *ui.Context, o LoadingScreenOptions) ui.Element { k := pixel.K(c) w := pixel.Or("LoadingScreen Width", o.Width, 240) title := k.Msg("loading") diff --git a/feedback/meter.go b/feedback/meter.go index f6e3537..aa4757c 100644 --- a/feedback/meter.go +++ b/feedback/meter.go @@ -16,7 +16,7 @@ type MeterOptions struct { } // Meter shows a level in Good, Warn or Bad. -func Meter(c *ui.Context, text string, value float64, o MeterOptions) *ui.Element { +func Meter(c *ui.Context, text string, value float64, o MeterOptions) ui.Element { k := pixel.K(c) lo, hi := o.Min, o.Max if lo == 0 && hi == 0 { diff --git a/feedback/progress.go b/feedback/progress.go index def7ca8..9a8d0bf 100644 --- a/feedback/progress.go +++ b/feedback/progress.go @@ -18,7 +18,7 @@ type ProgressOptions struct { } // Progress fills chunk by chunk; below zero a block runs. -func Progress(c *ui.Context, text string, value float64, o ProgressOptions) *ui.Element { +func Progress(c *ui.Context, text string, value float64, o ProgressOptions) ui.Element { k := pixel.K(c) if value > 1 || math.IsNaN(value) { panic(fmt.Sprintf("feedback: Progress value %v is past 1", value)) diff --git a/feedback/pulse.go b/feedback/pulse.go index d540088..07e26dc 100644 --- a/feedback/pulse.go +++ b/feedback/pulse.go @@ -12,7 +12,7 @@ type PulseOptions struct { } // Pulse rings its child with a ring growing in steps. -func Pulse(c *ui.Context, o PulseOptions, content func()) *ui.Element { +func Pulse(c *ui.Context, o PulseOptions, content func()) ui.Element { k := pixel.K(c) slot := o.Slot if slot == 0 { diff --git a/feedback/readygo.go b/feedback/readygo.go index ade3a75..e52b121 100644 --- a/feedback/readygo.go +++ b/feedback/readygo.go @@ -15,7 +15,7 @@ type ReadyGoOptions struct { } type ReadyGoResult struct { - Element *ui.Element + Element ui.Element // Done comes once, as GO! shows. Done bool } diff --git a/feedback/saveindicator.go b/feedback/saveindicator.go index 578c40c..ccd5add 100644 --- a/feedback/saveindicator.go +++ b/feedback/saveindicator.go @@ -15,7 +15,7 @@ type saveKey struct{} var spin = [4]int{12, 8, 2, 8} // SaveIndicator spins a floppy while saving, then shows SAVED ✓. -func SaveIndicator(c *ui.Context, saving bool, o SaveIndicatorOptions) *ui.Element { +func SaveIndicator(c *ui.Context, saving bool, o SaveIndicatorOptions) ui.Element { k := pixel.K(c) e := ui.Row(c).Gap(k.D(4)).AlignItems(ui.Center) ever := ui.Local(e, saveKey{}, func() bool { return false }) diff --git a/feedback/shake.go b/feedback/shake.go index 70a48c1..f2347d8 100644 --- a/feedback/shake.go +++ b/feedback/shake.go @@ -15,7 +15,7 @@ type ShakeOptions struct { var shakes = [...]int{2, -2, 2, -2, 1, -1, 0} // Shake jolts its child side to side briefly. -func Shake(c *ui.Context, o ShakeOptions, content func()) *ui.Element { +func Shake(c *ui.Context, o ShakeOptions, content func()) ui.Element { k := pixel.K(c) e := ui.Box(c) dx := shakes[k.Shot(c, e, o.Cue, 15, len(shakes)-1)] diff --git a/feedback/skeleton.go b/feedback/skeleton.go index c6c86fd..faee235 100644 --- a/feedback/skeleton.go +++ b/feedback/skeleton.go @@ -16,7 +16,7 @@ type SkeletonOptions struct { } // Skeleton stands in for content; a dithered shine sweeps it. -func Skeleton(c *ui.Context, o SkeletonOptions) *ui.Element { +func Skeleton(c *ui.Context, o SkeletonOptions) ui.Element { k := pixel.K(c) n := pixel.Or("Skeleton Lines", o.Lines, 3) w := pixel.Or("Skeleton Width", o.Width, 160) diff --git a/feedback/spinner.go b/feedback/spinner.go index b8cf918..1b9556a 100644 --- a/feedback/spinner.go +++ b/feedback/spinner.go @@ -15,7 +15,7 @@ type SpinnerOptions struct { var ring = [8][2]int{{5, 1}, {8, 2}, {9, 5}, {8, 8}, {5, 9}, {2, 8}, {1, 5}, {2, 2}} // Spinner turns through eight frames, even under Reduce Motion. -func Spinner(c *ui.Context, text string, o SpinnerOptions) *ui.Element { +func Spinner(c *ui.Context, text string, o SpinnerOptions) ui.Element { k := pixel.K(c) s := pixel.Or("Spinner Scale", o.Scale, 1) if s > 4 { diff --git a/feedback/successstate.go b/feedback/successstate.go index 7f0679d..bf97f53 100644 --- a/feedback/successstate.go +++ b/feedback/successstate.go @@ -16,7 +16,7 @@ type SuccessStateOptions struct { } // SuccessState is STAGE CLEAR! with stars popping in steps. -func SuccessState(c *ui.Context, message string, o SuccessStateOptions) *ui.Element { +func SuccessState(c *ui.Context, message string, o SuccessStateOptions) ui.Element { k := pixel.K(c) n := pixel.Or("SuccessState Stars", o.Stars, 3) col := ui.Column(c).Gap(k.D(8)).Padding(k.D(16)).AlignItems(ui.Center).Role(ui.RoleStatus).Label(k.Msg("stage_clear")) diff --git a/feedback/toasts.go b/feedback/toasts.go index 1c32c64..d3c9066 100644 --- a/feedback/toasts.go +++ b/feedback/toasts.go @@ -30,7 +30,7 @@ type ToastsOptions struct { type ToastsResult struct { // Element holds the toasts on screen. - Element *ui.Element + Element ui.Element // Acted is the toast whose action was clicked, else nil. Acted *Toast } diff --git a/files/archiveviewer.go b/files/archiveviewer.go index 1d3b415..ed52549 100644 --- a/files/archiveviewer.go +++ b/files/archiveviewer.go @@ -27,7 +27,7 @@ type ArchiveViewerOptions struct { } type ArchiveViewerResult struct { - Element *ui.Element + Element ui.Element // Sorted reports a header click this frame. Changed, Sorted bool } diff --git a/files/diskusage.go b/files/diskusage.go index 176cfab..f908918 100644 --- a/files/diskusage.go +++ b/files/diskusage.go @@ -25,7 +25,7 @@ type DiskUsageOptions struct { var useSlots = []pixel.Slot{pixel.Info, pixel.Good, pixel.Warn, pixel.Magic, pixel.Bad, pixel.Accent} // DiskUsage stacks each use in one bar over a legend. -func DiskUsage(c *ui.Context, text string, uses []Use, capacity int64, o DiskUsageOptions) *ui.Element { +func DiskUsage(c *ui.Context, text string, uses []Use, capacity int64, o DiskUsageOptions) ui.Element { k := pixel.K(c) if capacity <= 0 { panic(fmt.Sprintf("files: DiskUsage capacity %d", capacity)) diff --git a/files/filedetails.go b/files/filedetails.go index a6e1229..aff60ae 100644 --- a/files/filedetails.go +++ b/files/filedetails.go @@ -16,7 +16,7 @@ type FileDetailsOptions struct { } // FileDetails shows a file's icon, name, kind, size and dates. -func FileDetails(c *ui.Context, f File, o FileDetailsOptions) *ui.Element { +func FileDetails(c *ui.Context, f File, o FileDetailsOptions) ui.Element { k := pixel.K(c) check("FileDetails", f) return ui.Column(c).Width(k.D(pixel.Or("FileDetails Width", o.Width, 220))).Gap(k.D(6)).Role(ui.RoleGroup).Label(f.Name).Children(func() { diff --git a/files/fileexplorer.go b/files/fileexplorer.go index fc53024..ad3a888 100644 --- a/files/fileexplorer.go +++ b/files/fileexplorer.go @@ -26,7 +26,7 @@ type FileExplorerOptions struct { } type FileExplorerResult struct { - Element *ui.Element + Element ui.Element // Opened is Enter or a double click on a file. Changed, Opened bool } diff --git a/files/filegrid.go b/files/filegrid.go index 38fa179..626bd00 100644 --- a/files/filegrid.go +++ b/files/filegrid.go @@ -17,7 +17,7 @@ type FileGridOptions struct { } type FileGridResult struct { - Element *ui.Element + Element ui.Element // Opened is Enter or a double click on a file. Changed, Opened bool } diff --git a/files/fileicon.go b/files/fileicon.go index bc68409..525e474 100644 --- a/files/fileicon.go +++ b/files/fileicon.go @@ -14,7 +14,7 @@ type FileIconOptions struct { } // FileIcon draws a kind: a folder or a dog-eared page. -func FileIcon(c *ui.Context, kind Kind, o FileIconOptions) *ui.Element { +func FileIcon(c *ui.Context, kind Kind, o FileIconOptions) ui.Element { k := pixel.K(c) ka := look(kind) s := pixel.Or("FileIcon Scale", o.Scale, 1) diff --git a/files/filepreview.go b/files/filepreview.go index 19fccc2..ea084f3 100644 --- a/files/filepreview.go +++ b/files/filepreview.go @@ -24,7 +24,7 @@ type FilePreviewOptions struct { type previewKey struct{} // FilePreview shows a picture, else text, in a sunken pane. -func FilePreview(c *ui.Context, name string, pv Preview, o FilePreviewOptions) *ui.Element { +func FilePreview(c *ui.Context, name string, pv Preview, o FilePreviewOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("FilePreview W", o.W, 160), pixel.Or("FilePreview H", o.H, 120) return ui.Column(c).Gap(k.D(2)).AlignItems(ui.Start).Role(ui.RoleGroup).Label(name).Children(func() { diff --git a/files/memorycard.go b/files/memorycard.go index 11d7526..1d1c994 100644 --- a/files/memorycard.go +++ b/files/memorycard.go @@ -24,7 +24,7 @@ type MemoryCardOptions struct { } type MemoryCardResult struct { - Element *ui.Element + Element ui.Element Changed bool // Deleted is the block whose DELETE was pressed, else -1. Deleted int diff --git a/files/pathbar.go b/files/pathbar.go index fafd384..0012e86 100644 --- a/files/pathbar.go +++ b/files/pathbar.go @@ -17,7 +17,7 @@ type PathBarOptions struct { } type PathBarResult struct { - Element *ui.Element + Element ui.Element // Clicked is the folder clicked, else -1. Clicked int } @@ -48,7 +48,7 @@ func PathBar(c *ui.Context, text string, parts []string, o PathBarOptions) PathB }) break } - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(part).Disabled(o.Disabled) }) if base.Clicked() { diff --git a/files/quotameter.go b/files/quotameter.go index c02a73f..9816f83 100644 --- a/files/quotameter.go +++ b/files/quotameter.go @@ -33,7 +33,7 @@ var ( ) // QuotaMeter shows storage used as gems filling up. -func QuotaMeter(c *ui.Context, text string, used, quota int64, o QuotaMeterOptions) *ui.Element { +func QuotaMeter(c *ui.Context, text string, used, quota int64, o QuotaMeterOptions) ui.Element { k := pixel.K(c) n := pixel.Or("QuotaMeter Gems", o.Gems, 10) if n > 40 || used < 0 || quota <= 0 { diff --git a/files/recentfiles.go b/files/recentfiles.go index 83cb449..8d9aea7 100644 --- a/files/recentfiles.go +++ b/files/recentfiles.go @@ -18,7 +18,7 @@ type RecentFilesOptions struct { } type RecentFilesResult struct { - Element *ui.Element + Element ui.Element // Opened is Enter or a double click on a file. Changed, Opened bool } diff --git a/files/sharepanel.go b/files/sharepanel.go index f74479e..a4400af 100644 --- a/files/sharepanel.go +++ b/files/sharepanel.go @@ -28,7 +28,7 @@ type SharePanelOptions struct { } type SharePanelResult struct { - Element *ui.Element + Element ui.Element Copied, Changed bool } diff --git a/files/syncstatus.go b/files/syncstatus.go index d8dd025..d6ed557 100644 --- a/files/syncstatus.go +++ b/files/syncstatus.go @@ -88,7 +88,7 @@ var syncs = [...]struct { } // SyncStatus is a cloud: synced, syncing (arrows turning) or failed. -func SyncStatus(c *ui.Context, state SyncState, o SyncStatusOptions) *ui.Element { +func SyncStatus(c *ui.Context, state SyncState, o SyncStatusOptions) ui.Element { k := pixel.K(c) if state < Synced || state > SyncFailed { panic(fmt.Sprintf("files: SyncStatus state %d", state)) diff --git a/files/trash.go b/files/trash.go index bfc5432..1ee1b1a 100644 --- a/files/trash.go +++ b/files/trash.go @@ -15,7 +15,7 @@ type TrashOptions struct { } type TrashResult struct { - Element *ui.Element + Element ui.Element Emptied bool } diff --git a/files/uploadqueue.go b/files/uploadqueue.go index a70cfe7..8794cfb 100644 --- a/files/uploadqueue.go +++ b/files/uploadqueue.go @@ -29,7 +29,7 @@ type UploadQueueOptions struct { } type UploadQueueResult struct { - Element *ui.Element + Element ui.Element // Canceled is the upload whose ✕ was pressed, else -1. Canceled int } diff --git a/fx/aurora.go b/fx/aurora.go index e6103cd..96d185e 100644 --- a/fx/aurora.go +++ b/fx/aurora.go @@ -13,7 +13,7 @@ type AuroraOptions struct { } // Aurora hangs curtains whose slots slide sideways. -func Aurora(c *ui.Context, o AuroraOptions) *ui.Element { +func Aurora(c *ui.Context, o AuroraOptions) ui.Element { k := pixel.K(c) w, h := size("Aurora", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/clouds.go b/fx/clouds.go index 4f76a37..1ff241b 100644 --- a/fx/clouds.go +++ b/fx/clouds.go @@ -13,7 +13,7 @@ type CloudsOptions struct { } // Clouds drift by: far ones slowly, near ones faster. -func Clouds(c *ui.Context, o CloudsOptions) *ui.Element { +func Clouds(c *ui.Context, o CloudsOptions) ui.Element { k := pixel.K(c) w, h := size("Clouds", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/confetti.go b/fx/confetti.go index 6213689..d7cd401 100644 --- a/fx/confetti.go +++ b/fx/confetti.go @@ -16,7 +16,7 @@ type ConfettiOptions struct { var confettiSlots = []pixel.Slot{pixel.Accent, pixel.Good, pixel.Warn, pixel.Bad, pixel.Info, pixel.Magic} // Confetti tumbles bits of every color down, turning. -func Confetti(c *ui.Context, o ConfettiOptions) *ui.Element { +func Confetti(c *ui.Context, o ConfettiOptions) ui.Element { k := pixel.K(c) w, h := size("Confetti", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/dissolve.go b/fx/dissolve.go index 079a9df..b1f50d7 100644 --- a/fx/dissolve.go +++ b/fx/dissolve.go @@ -12,7 +12,7 @@ type DissolveOptions struct { } // Dissolve switches content's dots to a slot in shuffled order. -func Dissolve(c *ui.Context, covered bool, o DissolveOptions, content func()) *ui.Element { +func Dissolve(c *ui.Context, covered bool, o DissolveOptions, content func()) ui.Element { k := pixel.K(c) s := slotOr(o.Slot, dark(k)) e := ui.Column(c) diff --git a/fx/explosion.go b/fx/explosion.go index bdd87ab..8c9ab0e 100644 --- a/fx/explosion.go +++ b/fx/explosion.go @@ -22,7 +22,7 @@ const blastSteps = 8 var debrisSlots = []pixel.Slot{pixel.Warn, pixel.Bad, pixel.Faint} // Explosion bursts, breaks into smoke and throws debris. -func Explosion(c *ui.Context, o ExplosionOptions) *ui.Element { +func Explosion(c *ui.Context, o ExplosionOptions) ui.Element { k := pixel.K(c) n := pixel.Or("Explosion Size", o.Size, 48) e := box(c, k, o.Label, n, n) diff --git a/fx/fadedither.go b/fx/fadedither.go index 27b4071..b48df76 100644 --- a/fx/fadedither.go +++ b/fx/fadedither.go @@ -12,7 +12,7 @@ type FadeDitherOptions struct { } // FadeDither covers content through Bayer levels, or uncovers it. -func FadeDither(c *ui.Context, covered bool, o FadeDitherOptions, content func()) *ui.Element { +func FadeDither(c *ui.Context, covered bool, o FadeDitherOptions, content func()) ui.Element { k := pixel.K(c) s := slotOr(o.Slot, dark(k)) e := ui.Column(c) diff --git a/fx/fire.go b/fx/fire.go index 1ee4c88..1e6adbb 100644 --- a/fx/fire.go +++ b/fx/fire.go @@ -59,7 +59,7 @@ s###........###s `) // Fire burns a campfire: flicker frames, heat rising through it. -func Fire(c *ui.Context, o FireOptions) *ui.Element { +func Fire(c *ui.Context, o FireOptions) ui.Element { k := pixel.K(c) sc := scale("Fire", o.Scale, 2) return box(c, k, o.Label, 16*sc, 20*sc).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/fireflies.go b/fx/fireflies.go index 24f4da2..56856db 100644 --- a/fx/fireflies.go +++ b/fx/fireflies.go @@ -13,7 +13,7 @@ type FirefliesOptions struct { } // Fireflies wander and glow: bright, dim, then dark a while. -func Fireflies(c *ui.Context, o FirefliesOptions) *ui.Element { +func Fireflies(c *ui.Context, o FirefliesOptions) ui.Element { k := pixel.K(c) w, h := size("Fireflies", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/flash.go b/fx/flash.go index 1ad869f..83c085a 100644 --- a/fx/flash.go +++ b/fx/flash.go @@ -14,7 +14,7 @@ type FlashOptions struct { } // Flash fills content with a slot one step, then half. -func Flash(c *ui.Context, o FlashOptions, content func()) *ui.Element { +func Flash(c *ui.Context, o FlashOptions, content func()) ui.Element { k := pixel.K(c) s := slotOr(o.Slot, pixel.Lit) e := ui.Column(c) diff --git a/fx/fx.go b/fx/fx.go index 7199427..9b8ce12 100644 --- a/fx/fx.go +++ b/fx/fx.go @@ -10,7 +10,7 @@ import ( ) // box sizes an effect; a label makes it an image. -func box(c *ui.Context, k pixel.Kit, label string, w, h int) *ui.Element { +func box(c *ui.Context, k pixel.Kit, label string, w, h int) ui.Element { e := ui.Box(c).Size(k.D(w), k.D(h)) if label != "" { return e.Label(label).Role(ui.RoleImage) @@ -87,7 +87,7 @@ func checkHour(name string, hour int) { } // cover draws over e's content, clipped to e. -func cover(k pixel.Kit, e *ui.Element, draw func(g pixel.Canvas), content func()) *ui.Element { +func cover(k pixel.Kit, e ui.Element, draw func(g pixel.Canvas), content func()) ui.Element { return e.DrawOver(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) g.Clip(0, 0, g.W, g.H, func() { draw(g) }) diff --git a/fx/glitch.go b/fx/glitch.go index 232a75e..d1524ff 100644 --- a/fx/glitch.go +++ b/fx/glitch.go @@ -18,7 +18,7 @@ type GlitchOptions struct { const glitchSteps = 6 // Glitch shifts row bands and swaps two slots, briefly. -func Glitch(c *ui.Context, sp *pixel.Sprite, o GlitchOptions) *ui.Element { +func Glitch(c *ui.Context, sp *pixel.Sprite, o GlitchOptions) ui.Element { k := pixel.K(c) sc := scale("Glitch", o.Scale, 1) // Three dots either side hold the rows shifted out. diff --git a/fx/iriswipe.go b/fx/iriswipe.go index 8d67086..a5efe1c 100644 --- a/fx/iriswipe.go +++ b/fx/iriswipe.go @@ -14,7 +14,7 @@ type IrisWipeOptions struct { } // IrisWipe closes a circle on content's middle, or opens it. -func IrisWipe(c *ui.Context, closed bool, o IrisWipeOptions, content func()) *ui.Element { +func IrisWipe(c *ui.Context, closed bool, o IrisWipeOptions, content func()) ui.Element { k := pixel.K(c) s := slotOr(o.Slot, dark(k)) e := ui.Column(c) diff --git a/fx/lava.go b/fx/lava.go index d40e38b..b23e17f 100644 --- a/fx/lava.go +++ b/fx/lava.go @@ -13,7 +13,7 @@ type LavaOptions struct { } // Lava flows in cycling veins while bubbles swell and pop. -func Lava(c *ui.Context, o LavaOptions) *ui.Element { +func Lava(c *ui.Context, o LavaOptions) ui.Element { k := pixel.K(c) w, h := size("Lava", o.Width, o.Height, 160, 48) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/leaves.go b/fx/leaves.go index 17463d9..d851b7a 100644 --- a/fx/leaves.go +++ b/fx/leaves.go @@ -16,7 +16,7 @@ type LeavesOptions struct { var leafSlots = []pixel.Slot{pixel.Warn, pixel.Bad, pixel.Accent} // Leaves swing down side to side, turning on their stems. -func Leaves(c *ui.Context, o LeavesOptions) *ui.Element { +func Leaves(c *ui.Context, o LeavesOptions) ui.Element { k := pixel.K(c) w, h := size("Leaves", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/lightning.go b/fx/lightning.go index c89328b..82c234a 100644 --- a/fx/lightning.go +++ b/fx/lightning.go @@ -18,7 +18,7 @@ type LightningOptions struct { const strikeSteps = 5 // Lightning flashes, then forks a bolt to the ground. -func Lightning(c *ui.Context, o LightningOptions) *ui.Element { +func Lightning(c *ui.Context, o LightningOptions) ui.Element { k := pixel.K(c) w, h := size("Lightning", o.Width, o.Height, 96, 96) e := box(c, k, o.Label, w, h) diff --git a/fx/mosaic.go b/fx/mosaic.go index ffc0d7f..08b3b39 100644 --- a/fx/mosaic.go +++ b/fx/mosaic.go @@ -19,7 +19,7 @@ type MosaicOptions struct { var blocks = [...]int{1, 2, 3, 4, 6, 8, 6, 4, 3, 2, 1} // Mosaic pixelates sp in growing blocks, then sharpens it again. -func Mosaic(c *ui.Context, sp *pixel.Sprite, o MosaicOptions) *ui.Element { +func Mosaic(c *ui.Context, sp *pixel.Sprite, o MosaicOptions) ui.Element { k := pixel.K(c) sc := scale("Mosaic", o.Scale, 1) e := box(c, k, o.Label, sp.W*sc, sp.H*sc) diff --git a/fx/mountains.go b/fx/mountains.go index 0d601a6..69049e1 100644 --- a/fx/mountains.go +++ b/fx/mountains.go @@ -13,7 +13,7 @@ type MountainsOptions struct { } // Mountains scroll three ridges: far ones slow, near ones quick. -func Mountains(c *ui.Context, o MountainsOptions) *ui.Element { +func Mountains(c *ui.Context, o MountainsOptions) ui.Element { k := pixel.K(c) w, h := size("Mountains", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/ocean.go b/fx/ocean.go index efb5cfb..5dabec5 100644 --- a/fx/ocean.go +++ b/fx/ocean.go @@ -13,7 +13,7 @@ type OceanOptions struct { } // Ocean rolls wave crests toward you by cycling their slots. -func Ocean(c *ui.Context, o OceanOptions) *ui.Element { +func Ocean(c *ui.Context, o OceanOptions) ui.Element { k := pixel.K(c) w, h := size("Ocean", o.Width, o.Height, 160, 64) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/palettecycle.go b/fx/palettecycle.go index 7e899f8..c2247f1 100644 --- a/fx/palettecycle.go +++ b/fx/palettecycle.go @@ -18,7 +18,7 @@ type PaletteCycleOptions struct { } // PaletteCycle draws sp, rotating band's slots one place a step. -func PaletteCycle(c *ui.Context, sp *pixel.Sprite, band []pixel.Slot, o PaletteCycleOptions) *ui.Element { +func PaletteCycle(c *ui.Context, sp *pixel.Sprite, band []pixel.Slot, o PaletteCycleOptions) ui.Element { k := pixel.K(c) fps, sc := pixel.Or("PaletteCycle FPS", o.FPS, 8), scale("PaletteCycle", o.Scale, 1) if len(band) < 2 { diff --git a/fx/petals.go b/fx/petals.go index a65abcc..19ceaf9 100644 --- a/fx/petals.go +++ b/fx/petals.go @@ -13,7 +13,7 @@ type PetalsOptions struct { } // Petals fall on a breeze, turning as they go. -func Petals(c *ui.Context, o PetalsOptions) *ui.Element { +func Petals(c *ui.Context, o PetalsOptions) ui.Element { k := pixel.K(c) w, h := size("Petals", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/rain.go b/fx/rain.go index 3639c41..ad73bcd 100644 --- a/fx/rain.go +++ b/fx/rain.go @@ -13,7 +13,7 @@ type RainOptions struct { } // Rain slants streaks down; each splashes as it lands. -func Rain(c *ui.Context, o RainOptions) *ui.Element { +func Rain(c *ui.Context, o RainOptions) ui.Element { k := pixel.K(c) w, h := size("Rain", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/scanlines.go b/fx/scanlines.go index 39327c6..342872d 100644 --- a/fx/scanlines.go +++ b/fx/scanlines.go @@ -12,7 +12,7 @@ type ScanlinesOptions struct { } // Scanlines shades every other row of content at half strength. -func Scanlines(c *ui.Context, o ScanlinesOptions, content func()) *ui.Element { +func Scanlines(c *ui.Context, o ScanlinesOptions, content func()) ui.Element { k := pixel.K(c) s := slotOr(o.Slot, pixel.Shade) return cover(k, ui.Column(c), func(g pixel.Canvas) { diff --git a/fx/screenshake.go b/fx/screenshake.go index c6d82f3..64104e3 100644 --- a/fx/screenshake.go +++ b/fx/screenshake.go @@ -17,7 +17,7 @@ type ScreenShakeOptions struct { var quakes = [...][2]int{{3, -2}, {-3, 2}, {2, 3}, {-3, -2}, {2, 1}, {-1, -2}, {1, 1}, {-1, 0}, {0, 0}} // ScreenShake jolts its content both ways, settling as it goes. -func ScreenShake(c *ui.Context, o ScreenShakeOptions, content func()) *ui.Element { +func ScreenShake(c *ui.Context, o ScreenShakeOptions, content func()) ui.Element { k := pixel.K(c) a := pixel.Or("ScreenShake Amount", o.Amount, 3) e := ui.Box(c) diff --git a/fx/seasonscene.go b/fx/seasonscene.go index 3df6e71..d3195f8 100644 --- a/fx/seasonscene.go +++ b/fx/seasonscene.go @@ -15,14 +15,14 @@ type SeasonSceneOptions struct { } // SeasonScene paints a season at an hour, in its palette. -func SeasonScene(c *ui.Context, season pixel.Season, hour int, o SeasonSceneOptions) *ui.Element { +func SeasonScene(c *ui.Context, season pixel.Season, hour int, o SeasonSceneOptions) ui.Element { checkHour("SeasonScene", hour) if season > pixel.Winter { panic(fmt.Sprintf("fx: SeasonScene season %d", season)) } s := pixel.Current(c) s.Palette = pixel.Find(season, hour < 6 || hour >= 18) - var e *ui.Element + var e ui.Element pixel.Scope(c, s, func() { k := pixel.K(c) w, h := size("SeasonScene", o.Width, o.Height, 160, 96) diff --git a/fx/sky.go b/fx/sky.go index 494bbfa..d72efd4 100644 --- a/fx/sky.go +++ b/fx/sky.go @@ -13,7 +13,7 @@ type SkyOptions struct { } // Sky dithers down the palette's sky slots; night shows stars. -func Sky(c *ui.Context, o SkyOptions) *ui.Element { +func Sky(c *ui.Context, o SkyOptions) ui.Element { k := pixel.K(c) w, h := size("Sky", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/snow.go b/fx/snow.go index c524d34..0077a7a 100644 --- a/fx/snow.go +++ b/fx/snow.go @@ -13,7 +13,7 @@ type SnowOptions struct { } // Snow drifts flakes down, swaying; near ones are big, quick. -func Snow(c *ui.Context, o SnowOptions) *ui.Element { +func Snow(c *ui.Context, o SnowOptions) ui.Element { k := pixel.K(c) w, h := size("Snow", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/sparkle.go b/fx/sparkle.go index 05a3276..38966ed 100644 --- a/fx/sparkle.go +++ b/fx/sparkle.go @@ -20,7 +20,7 @@ type SparkleOptions struct { var glints = [...]int{0, 1, 2, 1, 0, -1, -1, -1} // Sparkle twinkles four stars around its middle, in turn. -func Sparkle(c *ui.Context, o SparkleOptions) *ui.Element { +func Sparkle(c *ui.Context, o SparkleOptions) ui.Element { k := pixel.K(c) n := pixel.Or("Sparkle Size", o.Size, 24) s := slotOr(o.Slot, pixel.Warn) diff --git a/fx/starfield.go b/fx/starfield.go index 4e17589..77e056b 100644 --- a/fx/starfield.go +++ b/fx/starfield.go @@ -13,7 +13,7 @@ type StarfieldOptions struct { } // Starfield scrolls three layers of stars, near ones fastest. -func Starfield(c *ui.Context, o StarfieldOptions) *ui.Element { +func Starfield(c *ui.Context, o StarfieldOptions) ui.Element { k := pixel.K(c) w, h := size("Starfield", o.Width, o.Height, 160, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/fx/sunmoon.go b/fx/sunmoon.go index 762787f..7b0c4d3 100644 --- a/fx/sunmoon.go +++ b/fx/sunmoon.go @@ -13,7 +13,7 @@ type SunMoonOptions struct { } // SunMoon arcs the sun by day, the moon by night. -func SunMoon(c *ui.Context, hour int, o SunMoonOptions) *ui.Element { +func SunMoon(c *ui.Context, hour int, o SunMoonOptions) ui.Element { checkHour("SunMoon", hour) k := pixel.K(c) w, h := size("SunMoon", o.Width, o.Height, 160, 96) diff --git a/fx/vignette.go b/fx/vignette.go index ff3fc82..2c9c2b7 100644 --- a/fx/vignette.go +++ b/fx/vignette.go @@ -12,7 +12,7 @@ type VignetteOptions struct { } // Vignette dithers a slot inward from edges; corners go solid. -func Vignette(c *ui.Context, o VignetteOptions, content func()) *ui.Element { +func Vignette(c *ui.Context, o VignetteOptions, content func()) ui.Element { k := pixel.K(c) s := slotOr(o.Slot, dark(k)) return cover(k, ui.Column(c), func(g pixel.Canvas) { vignette(g, s) }, content) diff --git a/fx/waterfall.go b/fx/waterfall.go index 9f7e6fd..30d5ef8 100644 --- a/fx/waterfall.go +++ b/fx/waterfall.go @@ -13,7 +13,7 @@ type WaterfallOptions struct { } // Waterfall pours between cliffs by cycling its water slots. -func Waterfall(c *ui.Context, o WaterfallOptions) *ui.Element { +func Waterfall(c *ui.Context, o WaterfallOptions) ui.Element { k := pixel.K(c) w, h := size("Waterfall", o.Width, o.Height, 64, 96) return box(c, k, o.Label, w, h).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/gallery/app.go b/gallery/app.go index 333c403..5fa6d9b 100644 --- a/gallery/app.go +++ b/gallery/app.go @@ -3,6 +3,7 @@ package gallery import ( "log/slog" "strings" + "time" "github.com/egoist/mygo/ui" @@ -22,6 +23,7 @@ type App struct { query string search input.TextInputResult logs map[string][]string + now func() time.Time } // New starts at path; the boot screen shows first. @@ -43,8 +45,11 @@ func (a *App) Go(path string) { } } +// SetClock gives the components a clock other than the frame's. +func (a *App) SetClock(now func() time.Time) { a.now = now } + func (a *App) View(c *ui.Context) { - pixel.Use(c, pixel.Settings{Palette: pixel.Palettes[a.palette], Dot: Dot, Locale: a.locale}) + pixel.Use(c, pixel.Settings{Palette: pixel.Palettes[a.palette], Dot: Dot, Locale: a.locale, Now: a.now}) if !a.started { a.boot(c) return diff --git a/gallery/boot.go b/gallery/boot.go index c7a85cb..45c9eed 100644 --- a/gallery/boot.go +++ b/gallery/boot.go @@ -20,7 +20,7 @@ func (a *App) boot(c *ui.Context) { shown = 16 } start := shown == 16 && (k.Reduce() || step%12 < 8) - shell, base, _ := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label("Press start") }) + shell, base, _ := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label("Press start") }) shell.Fill().Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) w := pixel.Measure(logo) * 4 @@ -34,7 +34,7 @@ func (a *App) boot(c *ui.Context) { hint := "Enter · Space · Click 回车 · 空格 · 点击" g.Text((g.W-pixel.Measure(hint))/2, g.H-28, hint, pixel.Faint, 1) }) - base.AutoFocus() + pixel.AutoFocus(c, base) if base.Clicked() || c.Shortcut(0, ui.KeyEnter) || c.Shortcut(0, ui.KeySpace) { slog.Debug("8bit: gallery start") a.started = true diff --git a/gallery/chrome.go b/gallery/chrome.go index bb15d52..e9a4aec 100644 --- a/gallery/chrome.go +++ b/gallery/chrome.go @@ -110,7 +110,7 @@ func (a *App) statusBar(c *ui.Context) { // press is a custom-drawn chrome tile; true when clicked. func press(c *ui.Context, w, h int, name string, draw func(g pixel.Canvas, st pixel.State)) bool { k := pixel.K(c) - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(name) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(name) }) if w > 0 { shell.Width(k.D(w)) } diff --git a/gallery/demos_foundations.go b/gallery/demos_foundations.go index 7805adc..619240d 100644 --- a/gallery/demos_foundations.go +++ b/gallery/demos_foundations.go @@ -30,7 +30,7 @@ func demoPalettes(c *ui.Context, log func(string)) { s.Palette = pal pixel.Scope(c, s, func() { kp := pixel.K(c) - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(pal.Name) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(pal.Name) }) shell.Height(kp.D(52)).Draw(func(p *ui.Painter, r ui.Rect) { g := kp.Canvas(p, r) g.Frame(0, 0, g.W, g.H, pixel.Window, pixel.Face) @@ -158,7 +158,7 @@ func demoSprites(c *ui.Context, log func(string)) { sp = slimeLeft } k := pixel.K(c) - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label("slime") }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label("slime") }) shell.Size(k.D(120), k.D(48)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) g.Rect(0, 40, g.W, 8, pixel.Good) diff --git a/gallery/demos_popups.go b/gallery/demos_popups.go index b393ab4..7a97c39 100644 --- a/gallery/demos_popups.go +++ b/gallery/demos_popups.go @@ -46,7 +46,7 @@ func demoCoachmark(c *ui.Context, log func(string)) { k := pixel.K(c) s := overlayState(c) tips := []string{"Your bag. Potions live here.", "The map. You are the red dot.", "Save often. Slimes are tricky."} - var targets [3]*ui.Element + var targets [3]ui.Element ui.Row(c).Gap(k.D(6)).Children(func() { for i, b := range []struct { art *pixel.Sprite @@ -71,7 +71,7 @@ func demoCoachmark(c *ui.Context, log func(string)) { func demoSpotlight(c *ui.Context, log func(string)) { k := pixel.K(c) s := overlayState(c) - var shop *ui.Element + var shop ui.Element ui.Row(c).Gap(k.D(6)).Children(func() { input.Button(c, "INN", input.ButtonOptions{}) shop = input.Button(c, "SHOP", input.ButtonOptions{Icon: icon.Cart}).Element diff --git a/gallery/registry.go b/gallery/registry.go index 4dd24e5..74c2816 100644 --- a/gallery/registry.go +++ b/gallery/registry.go @@ -106,7 +106,9 @@ func sources() map[string]string { if err != nil { panic(err) } - for _, part := range strings.Split(string(b), "\nfunc ")[1:] { + // A Windows checkout may bring CRLF. + src := strings.ReplaceAll(string(b), "\r\n", "\n") + for _, part := range strings.Split(src, "\nfunc ")[1:] { end := strings.Index(part, "\n}\n") if end < 0 { panic("gallery: unterminated func in " + f.Name()) diff --git a/game/achievements.go b/game/achievements.go index 101aa9b..b0d72ba 100644 --- a/game/achievements.go +++ b/game/achievements.go @@ -25,7 +25,7 @@ type AchievementsOptions struct { } type AchievementsResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/game/atbgauge.go b/game/atbgauge.go index d948fa3..f105652 100644 --- a/game/atbgauge.go +++ b/game/atbgauge.go @@ -15,7 +15,7 @@ type ATBGaugeOptions struct { } // ATBGauge fills as a turn nears; full, it flashes READY. -func ATBGauge(c *ui.Context, text string, value float64, o ATBGaugeOptions) *ui.Element { +func ATBGauge(c *ui.Context, text string, value float64, o ATBGaugeOptions) ui.Element { k := pixel.K(c) if !(value >= 0 && value <= 1) { panic(fmt.Sprintf("game: ATBGauge value %v outside 0–1", value)) diff --git a/game/battlelog.go b/game/battlelog.go index 479c875..97feaaa 100644 --- a/game/battlelog.go +++ b/game/battlelog.go @@ -18,7 +18,7 @@ type BattleLogOptions struct { } // BattleLog keeps the newest lines last; old ones roll off. -func BattleLog(c *ui.Context, text string, lines []Line, o BattleLogOptions) *ui.Element { +func BattleLog(c *ui.Context, text string, lines []Line, o BattleLogOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("BattleLog Width", o.Width, 180), pixel.Or("BattleLog Height", o.Height, 68) n := (h - 4) / pixel.LineHeight diff --git a/game/bossbar.go b/game/bossbar.go index e38023f..f6a128b 100644 --- a/game/bossbar.go +++ b/game/bossbar.go @@ -17,7 +17,7 @@ type BossBarOptions struct { } // BossBar is a wide bar under the name, phases marked. -func BossBar(c *ui.Context, name string, hp, full int, o BossBarOptions) *ui.Element { +func BossBar(c *ui.Context, name string, hp, full int, o BossBarOptions) ui.Element { k := pixel.K(c) amount("BossBar hp", hp, full) for _, f := range o.Phases { diff --git a/game/charactersheet.go b/game/charactersheet.go index 4ed2994..dee65f5 100644 --- a/game/charactersheet.go +++ b/game/charactersheet.go @@ -28,7 +28,7 @@ type Character struct { type CharacterSheetOptions struct{} // CharacterSheet shows a portrait, name, class, level and stats. -func CharacterSheet(c *ui.Context, h Character, o CharacterSheetOptions) *ui.Element { +func CharacterSheet(c *ui.Context, h Character, o CharacterSheetOptions) ui.Element { k := pixel.K(c) if h.Portrait == nil || h.Level < 1 { panic(fmt.Sprintf("game: character %q needs a portrait and a level (%d)", h.Name, h.Level)) diff --git a/game/coincounter.go b/game/coincounter.go index ddacbad..a30575a 100644 --- a/game/coincounter.go +++ b/game/coincounter.go @@ -16,7 +16,7 @@ type CoinCounterOptions struct { } // CoinCounter spins a coin beside a count that ticks up. -func CoinCounter(c *ui.Context, text string, coins int, o CoinCounterOptions) *ui.Element { +func CoinCounter(c *ui.Context, text string, coins int, o CoinCounterOptions) ui.Element { k := pixel.K(c) if coins < 0 { panic(fmt.Sprintf("game: CoinCounter %d", coins)) diff --git a/game/compass.go b/game/compass.go index 2c60a80..1555515 100644 --- a/game/compass.go +++ b/game/compass.go @@ -23,7 +23,7 @@ type CompassOptions struct { } // Compass is a heading strip with letters and markers. -func Compass(c *ui.Context, text string, heading float64, marks []Bearing, o CompassOptions) *ui.Element { +func Compass(c *ui.Context, text string, heading float64, marks []Bearing, o CompassOptions) ui.Element { k := pixel.K(c) for _, v := range append([]float64{heading}, bearings(marks)...) { if math.IsNaN(v) || math.IsInf(v, 0) { diff --git a/game/crafting.go b/game/crafting.go index f760532..44b44ad 100644 --- a/game/crafting.go +++ b/game/crafting.go @@ -22,7 +22,7 @@ type CraftingOptions struct { } type CraftingResult struct { - Element *ui.Element + Element ui.Element Changed bool // Picked is the cell chosen this frame, or -1. Picked int diff --git a/game/credits.go b/game/credits.go index 9092ab1..e1a779a 100644 --- a/game/credits.go +++ b/game/credits.go @@ -22,7 +22,7 @@ type CreditsOptions struct { } type CreditsResult struct { - Element *ui.Element + Element ui.Element // Done is true in the frame the roll ends. Done bool } diff --git a/game/damagenumbers.go b/game/damagenumbers.go index bf84dac..65f65ce 100644 --- a/game/damagenumbers.go +++ b/game/damagenumbers.go @@ -28,7 +28,7 @@ type DamageNumbersOptions struct { const life = 800 * time.Millisecond // DamageNumbers floats hits up, dissolves, then drops them. -func DamageNumbers(c *ui.Context, text string, hits *[]Hit, o DamageNumbersOptions) *ui.Element { +func DamageNumbers(c *ui.Context, text string, hits *[]Hit, o DamageNumbersOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("DamageNumbers Width", o.Width, 160), pixel.Or("DamageNumbers Height", o.Height, 80) // S5: hits rise in whole frames, fifteen a second. diff --git a/game/endingcard.go b/game/endingcard.go index 21da4ad..2d37dfa 100644 --- a/game/endingcard.go +++ b/game/endingcard.go @@ -16,7 +16,7 @@ type EndingCardOptions struct { } // EndingCard dithers THE END in, step by step. -func EndingCard(c *ui.Context, o EndingCardOptions) *ui.Element { +func EndingCard(c *ui.Context, o EndingCardOptions) ui.Element { k := pixel.K(c) w, h := pixel.Or("EndingCard Width", o.Width, 200), pixel.Or("EndingCard Height", o.Height, 80) s := k.Msg("the_end") diff --git a/game/equipment.go b/game/equipment.go index 71a0b53..1665e84 100644 --- a/game/equipment.go +++ b/game/equipment.go @@ -38,7 +38,7 @@ type EquipmentOptions struct { } type EquipmentResult struct { - Element *ui.Element + Element ui.Element Changed bool // Picked is the slot opened this frame, or -1. Picked int @@ -61,7 +61,7 @@ func Equipment(c *ui.Context, text string, gear []data.Item, selected *int, o Eq } w = max(w, pixel.Measure(captions[i])) } - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) arrows(base, spots, selected) diff --git a/game/expbar.go b/game/expbar.go index 92edace..d02ba8b 100644 --- a/game/expbar.go +++ b/game/expbar.go @@ -23,7 +23,7 @@ type rise struct { } // ExpBar shows xp toward next and the level; ups flash. -func ExpBar(c *ui.Context, text string, level, xp, next int, o ExpBarOptions) *ui.Element { +func ExpBar(c *ui.Context, text string, level, xp, next int, o ExpBarOptions) ui.Element { k := pixel.K(c) if level < 1 { panic(fmt.Sprintf("game: ExpBar level %d", level)) diff --git a/game/gachareel.go b/game/gachareel.go index eb26eb5..2adefbf 100644 --- a/game/gachareel.go +++ b/game/gachareel.go @@ -14,7 +14,7 @@ import ( type GachaReelOptions struct{} type GachaReelResult struct { - Element *ui.Element + Element ui.Element // Done is true in the frame the last reel stops. Done bool } diff --git a/game/game.go b/game/game.go index 7dd6177..d26b21e 100644 --- a/game/game.go +++ b/game/game.go @@ -88,7 +88,7 @@ func nearest(spots []Spot, from, dx, dy int) int { } // arrows moves cur among spots by arrow keys. -func arrows(base *ui.Element, spots []Spot, cur *int) { +func arrows(base ui.Element, spots []Spot, cur *int) { for _, m := range []struct { key ui.Key dx, dy int @@ -115,7 +115,7 @@ type begun struct { } // start is e's first showing, or its last cue change. -func start(k pixel.Kit, e *ui.Element, cue int) begun { +func start(k pixel.Kit, e ui.Element, cue int) begun { s := ui.Local(e, startKey{}, func() begun { return begun{cue: cue, at: k.Now()} }) if s.cue != cue { s.cue, s.at, s.run = cue, k.Now(), s.run+1 diff --git a/game/gameover.go b/game/gameover.go index 24e100f..9dc0070 100644 --- a/game/gameover.go +++ b/game/gameover.go @@ -14,7 +14,7 @@ type GameOverOptions struct { } type GameOverResult struct { - Element *ui.Element + Element ui.Element Continue, Quit bool } diff --git a/game/healthbar.go b/game/healthbar.go index 72e5e2b..fe50d14 100644 --- a/game/healthbar.go +++ b/game/healthbar.go @@ -26,7 +26,7 @@ type trail struct { const hold = 300 * time.Millisecond // HealthBar shows hp with a lagging trail; low HP blinks. -func HealthBar(c *ui.Context, text string, hp, full int, o HealthBarOptions) *ui.Element { +func HealthBar(c *ui.Context, text string, hp, full int, o HealthBarOptions) ui.Element { k := pixel.K(c) amount("HealthBar hp", hp, full) w := pixel.Or("HealthBar Width", o.Width, 96) diff --git a/game/hearts.go b/game/hearts.go index 88daac3..2123257 100644 --- a/game/hearts.go +++ b/game/hearts.go @@ -17,7 +17,7 @@ type HeartsOptions struct { } // Hearts shows hp of full hearts, in quarters. -func Hearts(c *ui.Context, text string, hp, full float64, o HeartsOptions) *ui.Element { +func Hearts(c *ui.Context, text string, hp, full float64, o HeartsOptions) ui.Element { k := pixel.K(c) if full < 1 || full != math.Trunc(full) || !(hp >= 0 && hp <= full) || hp*4 != math.Trunc(hp*4) { panic(fmt.Sprintf("game: Hearts %v of %v; hearts are whole, health in quarters", hp, full)) diff --git a/game/hotbar.go b/game/hotbar.go index 2500b65..1856d2c 100644 --- a/game/hotbar.go +++ b/game/hotbar.go @@ -16,7 +16,7 @@ type HotbarOptions struct { } type HotbarResult struct { - Element *ui.Element + Element ui.Element Changed bool // Used is the slot used this frame, or -1. Used int diff --git a/game/interactprompt.go b/game/interactprompt.go index 6c14c78..6cc6e87 100644 --- a/game/interactprompt.go +++ b/game/interactprompt.go @@ -15,7 +15,7 @@ type InteractPromptOptions struct { } // InteractPrompt bobs a Ⓐ TALK bubble above target. -func InteractPrompt(c *ui.Context, target *ui.Element, action string, o InteractPromptOptions) *ui.Element { +func InteractPrompt(c *ui.Context, target ui.Element, action string, o InteractPromptOptions) ui.Element { k := pixel.K(c) key := o.Key if key == "" { @@ -33,7 +33,7 @@ func InteractPrompt(c *ui.Context, target *ui.Element, action string, o Interact if !k.Reduce() { bob = k.Tick(c, 4) / 2 % 2 } - var e *ui.Element + var e ui.Element ui.Overlay(c, func() { e = ui.Box(c).Absolute().Left(x).Bottom(wh - b.Y + k.D(1+bob)).PassThrough().Role(ui.RoleText).Label(key + " " + action) // Bounds lag a layout; if the target moved, build again. diff --git a/game/letterbox.go b/game/letterbox.go index 5b79218..f371c18 100644 --- a/game/letterbox.go +++ b/game/letterbox.go @@ -13,10 +13,10 @@ type LetterboxOptions struct { } // Letterbox slides cutscene bars over content in four steps. -func Letterbox(c *ui.Context, on bool, o LetterboxOptions, content func()) *ui.Element { +func Letterbox(c *ui.Context, on bool, o LetterboxOptions, content func()) ui.Element { k := pixel.K(c) bar := pixel.Or("Letterbox Bar", o.Bar, 16) - var cover *ui.Element + var cover ui.Element e := layout.Layers(c, layout.LayersOptions{}, content, func() { cover = ui.Box(c).Fill().PassThrough() }) h := bar * k.Toward(c, e, on, 8, 4) / 4 cover.Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/game/levelupscreen.go b/game/levelupscreen.go index 69d6cd3..7a558ff 100644 --- a/game/levelupscreen.go +++ b/game/levelupscreen.go @@ -26,7 +26,7 @@ type LevelUpScreenOptions struct { } type LevelUpScreenResult struct { - Element *ui.Element + Element ui.Element Changed bool Done bool } diff --git a/game/lives.go b/game/lives.go index c547ced..e7a9f88 100644 --- a/game/lives.go +++ b/game/lives.go @@ -15,7 +15,7 @@ type LivesOptions struct { } // Lives shows a life icon and ×count. -func Lives(c *ui.Context, text string, n int, o LivesOptions) *ui.Element { +func Lives(c *ui.Context, text string, n int, o LivesOptions) ui.Element { k := pixel.K(c) if n < 0 { panic(fmt.Sprintf("game: Lives %d", n)) diff --git a/game/loadingtips.go b/game/loadingtips.go index f33ae7a..471a4d7 100644 --- a/game/loadingtips.go +++ b/game/loadingtips.go @@ -18,7 +18,7 @@ type LoadingTipsOptions struct { const tipTurn = 4 * time.Second // LoadingTips turns tips every 4 s under a progress bar. -func LoadingTips(c *ui.Context, tips []string, value float64, o LoadingTipsOptions) *ui.Element { +func LoadingTips(c *ui.Context, tips []string, value float64, o LoadingTipsOptions) ui.Element { k := pixel.K(c) w := pixel.Or("LoadingTips Width", o.Width, 200) if len(tips) == 0 { diff --git a/game/lootpopup.go b/game/lootpopup.go index cbc1e5e..7dc67db 100644 --- a/game/lootpopup.go +++ b/game/lootpopup.go @@ -15,7 +15,7 @@ type LootPopupOptions struct { } type LootPopupResult struct { - Element *ui.Element + Element ui.Element // Closed is OK, Escape or a click outside. Closed bool } diff --git a/game/manabar.go b/game/manabar.go index 171d9f0..b7daedc 100644 --- a/game/manabar.go +++ b/game/manabar.go @@ -14,7 +14,7 @@ type ManaBarOptions struct { } // ManaBar shows mp of full, ticked every few points. -func ManaBar(c *ui.Context, text string, mp, full int, o ManaBarOptions) *ui.Element { +func ManaBar(c *ui.Context, text string, mp, full int, o ManaBarOptions) ui.Element { k := pixel.K(c) amount("ManaBar mp", mp, full) w := pixel.Or("ManaBar Width", o.Width, 96) diff --git a/game/menus_test.go b/game/menus_test.go index 7a2f0e7..c5be6b1 100644 --- a/game/menus_test.go +++ b/game/menus_test.go @@ -262,7 +262,7 @@ func TestLoadingTipsTurnEveryFourSeconds(t *testing.T) { func TestInteractPromptBobsOnceASecond(t *testing.T) { wd := &world{clk: newClock()} tt := view(300, 200, wd, func(c *ui.Context) { - var target *ui.Element + var target ui.Element ui.Column(c).Padding(60).Children(func() { target = ui.Box(c).Size(20, 20) }) game.InteractPrompt(c, target, "OPEN", game.InteractPromptOptions{}) }) diff --git a/game/minimap.go b/game/minimap.go index 3eb41f9..02c2341 100644 --- a/game/minimap.go +++ b/game/minimap.go @@ -27,7 +27,7 @@ type MinimapOptions struct { var tiles = map[rune]pixel.Slot{'#': pixel.Edge, '.': pixel.Well, '~': pixel.Info, '"': pixel.Good, '^': pixel.Faint, ' ': pixel.Back} // Minimap draws tiles, fog where unseen, player and marks. -func Minimap(c *ui.Context, text string, rows []string, seen [][]bool, player Spot, marks []Marker, o MinimapOptions) *ui.Element { +func Minimap(c *ui.Context, text string, rows []string, seen [][]bool, player Spot, marks []Marker, o MinimapOptions) ui.Element { k := pixel.K(c) cell := pixel.Or("Minimap Cell", o.Cell, 3) if len(rows) == 0 { diff --git a/game/missionbrief.go b/game/missionbrief.go index 7cddca7..a8ed26c 100644 --- a/game/missionbrief.go +++ b/game/missionbrief.go @@ -14,7 +14,7 @@ type MissionBriefOptions struct { } type MissionBriefResult struct { - Element *ui.Element + Element ui.Element Start bool } diff --git a/game/optionsmenu.go b/game/optionsmenu.go index 9e3bdac..344dfd8 100644 --- a/game/optionsmenu.go +++ b/game/optionsmenu.go @@ -39,7 +39,7 @@ type OptionsMenuOptions struct { } type OptionsMenuResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -63,7 +63,7 @@ func OptionsMenu(c *ui.Context, text string, settings []Setting, row *int, o Opt value = max(value, pixel.Measure(ch)) } } - 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.RoleList).Label(text).Disabled(o.Disabled) }) res, was := OptionsMenuResult{Element: shell}, *row diff --git a/game/partyframes.go b/game/partyframes.go index d510b63..b44f0a5 100644 --- a/game/partyframes.go +++ b/game/partyframes.go @@ -18,7 +18,7 @@ type Member struct { type PartyFramesOptions struct{} // PartyFrames stacks member cards; a fallen member greys out. -func PartyFrames(c *ui.Context, text string, members []Member, o PartyFramesOptions) *ui.Element { +func PartyFrames(c *ui.Context, text string, members []Member, o PartyFramesOptions) ui.Element { k := pixel.K(c) return ui.Column(c).Gap(k.D(3)).AlignItems(ui.Start).Role(ui.RoleGroup).Label(text).Children(func() { for _, m := range members { diff --git a/game/pressstart.go b/game/pressstart.go index 773d8c4..68fcd9e 100644 --- a/game/pressstart.go +++ b/game/pressstart.go @@ -13,7 +13,7 @@ type PressStartOptions struct { } type PressStartResult struct { - Element *ui.Element + Element ui.Element Pressed bool } @@ -21,7 +21,7 @@ type PressStartResult struct { func PressStart(c *ui.Context, o PressStartOptions) PressStartResult { k := pixel.K(c) s := k.Msg("press_start") - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(s).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(s).Disabled(o.Disabled) }) res := PressStartResult{Element: shell, Pressed: base.Clicked()} if res.Pressed { slog.Debug("8bit: press start") diff --git a/game/questlog.go b/game/questlog.go index e2ac74b..3046240 100644 --- a/game/questlog.go +++ b/game/questlog.go @@ -16,7 +16,7 @@ type QuestLogOptions struct { } type QuestLogResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/game/questtracker.go b/game/questtracker.go index fbaa0fc..adfa806 100644 --- a/game/questtracker.go +++ b/game/questtracker.go @@ -12,7 +12,7 @@ import ( type QuestTrackerOptions struct{} // QuestTracker lists a quest's objectives with counts, met ones ticked. -func QuestTracker(c *ui.Context, q Quest, o QuestTrackerOptions) *ui.Element { +func QuestTracker(c *ui.Context, q Quest, o QuestTrackerOptions) ui.Element { k := pixel.K(c) q.check() w := icon.Star.W + 2 + pixel.Measure(q.Title) diff --git a/game/resultscreen.go b/game/resultscreen.go index ee1d2a2..8f0e584 100644 --- a/game/resultscreen.go +++ b/game/resultscreen.go @@ -12,7 +12,7 @@ import ( type ResultScreenOptions struct{} // ResultScreen counts time and score up, then shows the rank. -func ResultScreen(c *ui.Context, text string, took time.Duration, score int, rank string, o ResultScreenOptions) *ui.Element { +func ResultScreen(c *ui.Context, text string, took time.Duration, score int, rank string, o ResultScreenOptions) ui.Element { k := pixel.K(c) if took < 0 || score < 0 || rank == "" { panic(fmt.Sprintf("game: ResultScreen %v, %d, rank %q", took, score, rank)) diff --git a/game/rewards_test.go b/game/rewards_test.go index 57829fd..7a99b2b 100644 --- a/game/rewards_test.go +++ b/game/rewards_test.go @@ -262,7 +262,7 @@ func TestGachaReelRefusesAFaceWithoutSprite(t *testing.T) { } func TestInteractPromptSitsAboveItsTarget(t *testing.T) { - var target *ui.Element + var target ui.Element pad := 40 tt := view(400, 300, &world{clk: newClock(), reduce: true}, func(c *ui.Context) { ui.Column(c).Padding(pixel.K(c).D(pad)).Children(func() { diff --git a/game/saveslots.go b/game/saveslots.go index daca104..de1581e 100644 --- a/game/saveslots.go +++ b/game/saveslots.go @@ -22,7 +22,7 @@ type SaveSlotsOptions struct { } type SaveSlotsResult struct { - Element *ui.Element + Element ui.Element Changed bool // Chosen is the slot opened this frame, or -1. Chosen int diff --git a/game/scoreboard.go b/game/scoreboard.go index 6c49aee..6210d7b 100644 --- a/game/scoreboard.go +++ b/game/scoreboard.go @@ -14,7 +14,7 @@ type ScoreBoardOptions struct { } // ScoreBoard shows SCORE and HI-SCORE in big digits. -func ScoreBoard(c *ui.Context, score, hi int, o ScoreBoardOptions) *ui.Element { +func ScoreBoard(c *ui.Context, score, hi int, o ScoreBoardOptions) ui.Element { k := pixel.K(c) if score < 0 || hi < 0 { panic(fmt.Sprintf("game: ScoreBoard %d and %d", score, hi)) diff --git a/game/shop.go b/game/shop.go index 3cb7692..b7ae950 100644 --- a/game/shop.go +++ b/game/shop.go @@ -24,7 +24,7 @@ type ShopOptions struct { } type ShopResult struct { - Element *ui.Element + Element ui.Element // Bought and Sold are this frame's trades, or -1. Bought, Sold int } diff --git a/game/skillbutton.go b/game/skillbutton.go index 8cf6aa7..2b5e23b 100644 --- a/game/skillbutton.go +++ b/game/skillbutton.go @@ -16,7 +16,7 @@ type SkillButtonOptions struct { } type SkillButtonResult struct { - Element *ui.Element + Element ui.Element // Clicked is a press while the skill is ready. Clicked bool } @@ -27,7 +27,7 @@ func SkillButton(c *ui.Context, text string, sprite *pixel.Sprite, key string, c if sprite == nil || !(cooldown >= 0 && cooldown <= 1) || !pixel.MicroOK(key) { panic(fmt.Sprintf("game: SkillButton %q needs a sprite, a micro key %q, a cooldown in 0–1 (%v)", text, key, cooldown)) } - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) }) res := SkillButtonResult{Element: shell, Clicked: base.Clicked() && cooldown == 0} if res.Clicked { slog.Debug("8bit: skill", "label", text) diff --git a/game/skilltree.go b/game/skilltree.go index 76b7341..077edf9 100644 --- a/game/skilltree.go +++ b/game/skilltree.go @@ -25,7 +25,7 @@ type SkillTreeOptions struct { } type SkillTreeResult struct { - Element *ui.Element + Element ui.Element Changed bool // Learned is the skill learned this frame, or -1. Learned int @@ -69,7 +69,7 @@ func SkillTree(c *ui.Context, text string, skills []Skill, cursor, points *int, return !skills[i].Learned } old := *cursor - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) arrows(base, spots, cursor) diff --git a/game/staminabar.go b/game/staminabar.go index 668f01d..5005782 100644 --- a/game/staminabar.go +++ b/game/staminabar.go @@ -15,7 +15,7 @@ type StaminaBarOptions struct { } // StaminaBar blocks stamina out; the part-full block dithers. -func StaminaBar(c *ui.Context, text string, value float64, o StaminaBarOptions) *ui.Element { +func StaminaBar(c *ui.Context, text string, value float64, o StaminaBarOptions) ui.Element { k := pixel.K(c) if !(value >= 0 && value <= 1) { panic(fmt.Sprintf("game: StaminaBar value %v outside 0–1", value)) diff --git a/game/statuseffects.go b/game/statuseffects.go index 2a854b0..e602f6a 100644 --- a/game/statuseffects.go +++ b/game/statuseffects.go @@ -22,7 +22,7 @@ type Effect struct { type StatusEffectsOptions struct{} // StatusEffects rows effect icons: buffs edged Good, debuffs Bad. -func StatusEffects(c *ui.Context, text string, effects []Effect, o StatusEffectsOptions) *ui.Element { +func StatusEffects(c *ui.Context, text string, effects []Effect, o StatusEffectsOptions) ui.Element { k := pixel.K(c) parts := make([]string, len(effects)) for i, ef := range effects { diff --git a/game/targetselector.go b/game/targetselector.go index 310dbbd..58448d3 100644 --- a/game/targetselector.go +++ b/game/targetselector.go @@ -21,7 +21,7 @@ type TargetSelectorOptions struct { } type TargetSelectorResult struct { - Element *ui.Element + Element ui.Element Changed bool // Chosen is the target confirmed this frame, or -1. Chosen int diff --git a/game/timerhud.go b/game/timerhud.go index d464fce..f291c30 100644 --- a/game/timerhud.go +++ b/game/timerhud.go @@ -13,7 +13,7 @@ import ( type TimerHUDOptions struct{} // TimerHUD shows TIME in seconds; under 30 it blinks red. -func TimerHUD(c *ui.Context, left time.Duration, o TimerHUDOptions) *ui.Element { +func TimerHUD(c *ui.Context, left time.Duration, o TimerHUDOptions) ui.Element { k := pixel.K(c) if left < 0 { panic(fmt.Sprintf("game: TimerHUD %v left", left)) diff --git a/game/titlescreen.go b/game/titlescreen.go index 888ff31..4a56fc4 100644 --- a/game/titlescreen.go +++ b/game/titlescreen.go @@ -16,7 +16,7 @@ type TitleScreenOptions struct { } type TitleScreenResult struct { - Element *ui.Element + Element ui.Element // Chosen is 0 NEW GAME, 1 CONTINUE, 2 OPTIONS. Chosen int } @@ -50,7 +50,7 @@ func TitleScreen(c *ui.Context, title string, started *bool, o TitleScreenOption } // logo writes the title big, gold over red, shadowed. -func logo(c *ui.Context, title string) *ui.Element { +func logo(c *ui.Context, title string) ui.Element { k := pixel.K(c) w := 3*pixel.Measure(title) + 2 return ui.Box(c).Role(ui.RoleText).Label(title).Size(k.D(w), k.D(3*pixel.LineHeight+2)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/game/treasurechest.go b/game/treasurechest.go index e325439..d0b5725 100644 --- a/game/treasurechest.go +++ b/game/treasurechest.go @@ -16,7 +16,7 @@ type TreasureChestOptions struct { } type TreasureChestResult struct { - Element *ui.Element + Element ui.Element // Opened is true in the frame a press opens it. Opened bool } @@ -35,7 +35,7 @@ func TreasureChest(c *ui.Context, text string, it data.Item, open *bool, o Treas if it.Sprite == nil { panic(fmt.Sprintf("game: TreasureChest item %q has no sprite", it.Name)) } - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) }) res := TreasureChestResult{Element: shell} if base.Clicked() && !*open { *open, res.Opened = true, true diff --git a/game/turnorder.go b/game/turnorder.go index cf7b9d3..6b3eb32 100644 --- a/game/turnorder.go +++ b/game/turnorder.go @@ -19,7 +19,7 @@ type Fighter struct { type TurnOrderOptions struct{} // TurnOrder rows portraits in order, the active raised. -func TurnOrder(c *ui.Context, text string, order []Fighter, active int, o TurnOrderOptions) *ui.Element { +func TurnOrder(c *ui.Context, text string, order []Fighter, active int, o TurnOrderOptions) ui.Element { k := pixel.K(c) if active < 0 || active >= len(order) { panic(fmt.Sprintf("game: TurnOrder active %d of %d", active, len(order))) diff --git a/go.mod b/go.mod index a48ec9a..f090149 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI go 1.27.1 require ( - github.com/egoist/mygo v0.2.10 + github.com/egoist/mygo v0.5.5 rsc.io/qr v0.2.0 ) @@ -11,4 +11,5 @@ require ( github.com/ebitengine/purego v0.11.1 // indirect github.com/go-text/typesetting v0.3.5 // indirect golang.org/x/image v0.46.0 // indirect + golang.org/x/text v0.43.0 // indirect ) diff --git a/go.sum b/go.sum index ce94117..401be03 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,14 @@ github.com/ebitengine/purego v0.11.1 h1:2zpWRSQNVKN4eKsKO9eM1ILDgWfYMY9GwqRmK6XeQ/0= github.com/ebitengine/purego v0.11.1/go.mod h1:DCHPP08djqhNSoTfImcnHYQRZmd0qhakvrozqaEYhGQ= -github.com/egoist/mygo v0.2.10 h1:Mfe6SpQngC9kLjrejpGM7L/k3Q62BdBAyJ8MrHfL6bQ= -github.com/egoist/mygo v0.2.10/go.mod h1:1sRzZ6jEhKE7iuEQXjyoXDgxyxQ1uTgMHDcDKZvoDIU= +github.com/egoist/mygo v0.5.5 h1:3pvlEwumsck/L99cbDpay/Eqcf51B7zhKNXekiodLOw= +github.com/egoist/mygo v0.5.5/go.mod h1:daHGetyAlBkHhIZOOIcAI7C82yLUY7HgeT2+Z6w2VPU= github.com/go-text/typesetting v0.3.5 h1:XZPUooClHY0Vf/rFyUyuPRNEkawARaFzLMQcXLSEyPk= github.com/go-text/typesetting v0.3.5/go.mod h1:XZO1hD+nQVyvVa5IicQk7FsCa4PFQaJ2soWAP1f//68= github.com/go-text/typesetting-utils v0.0.0-20260419141703-4ffe8874dabc h1:8FGo2It5K75XkavhTiCKExUfVaVDS1feBnLCru5qeoY= github.com/go-text/typesetting-utils v0.0.0-20260419141703-4ffe8874dabc/go.mod h1:3/62I4La/HBRX9TcTpBj4eipLiwzf+vhI+7whTc9V7o= golang.org/x/image v0.46.0 h1:b1+oYj0Jbp6K5MDT4i4/eZpYlk3V8SJhhDKh6LBHAyQ= golang.org/x/image v0.46.0/go.mod h1:3B3W05VGVQyuXucLINLjXKrqISASfi4Xj+iCVkLMwew= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= diff --git a/input/autocomplete.go b/input/autocomplete.go index ba7fed8..3c624b2 100644 --- a/input/autocomplete.go +++ b/input/autocomplete.go @@ -18,7 +18,7 @@ type AutocompleteOptions struct { } type AutocompleteResult struct { - Element *ui.Element + Element ui.Element // Accepted reports a completion taken by Tab or Enter. Changed, Accepted, Submitted bool } diff --git a/input/button.go b/input/button.go index edc1601..5079f0c 100644 --- a/input/button.go +++ b/input/button.go @@ -20,9 +20,9 @@ type ButtonOptions struct { } type ButtonResult struct { - Element *ui.Element + Element ui.Element // Base is the focusable inside Element. - Base *ui.Element + Base ui.Element Clicked bool } @@ -31,7 +31,7 @@ func Button(c *ui.Context, text string, o ButtonOptions) ButtonResult { k := pixel.K(c) h, pad, top, sc := o.Size.metrics() name := label(o.Label, text) - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(name).Disabled(o.Disabled || o.Loading) }) w := pixel.Measure(text) * sc diff --git a/input/buttongroup.go b/input/buttongroup.go index 5111394..0110cf1 100644 --- a/input/buttongroup.go +++ b/input/buttongroup.go @@ -17,7 +17,7 @@ type ButtonGroupOptions struct { } type ButtonGroupResult struct { - Element *ui.Element + Element ui.Element // Clicked is the button clicked this frame, or -1. Clicked int } @@ -29,7 +29,7 @@ func ButtonGroup(c *ui.Context, labels []string, o ButtonGroupOptions) ButtonGro res := ButtonGroupResult{Clicked: -1} res.Element = ui.Row(c).Role(ui.RoleToolbar).Label(o.Label).FocusGroup(ui.Horizontal).Children(func() { for i, l := range labels { - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(l).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(l).Disabled(o.Disabled) }) w := pixel.Measure(l)*sc + 2*pad if i > 0 { shell.Margin(0, 0, 0, k.D(-1)) diff --git a/input/cheatcode.go b/input/cheatcode.go index bf7c27b..f508ba2 100644 --- a/input/cheatcode.go +++ b/input/cheatcode.go @@ -17,7 +17,7 @@ type CheatCodeOptions struct { } type CheatCodeResult struct { - Element *ui.Element + Element ui.Element Unlocked bool } @@ -38,7 +38,7 @@ func CheatCode(c *ui.Context, text string, o CheatCodeOptions) CheatCodeResult { if seq == nil { seq = konami } - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) s := ui.Local(base, cheatKey{}, func() cheat { return cheat{} }) diff --git a/input/checkbox.go b/input/checkbox.go index 178e700..44485aa 100644 --- a/input/checkbox.go +++ b/input/checkbox.go @@ -26,7 +26,7 @@ type CheckboxOptions struct { } type CheckboxResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -35,7 +35,7 @@ func Checkbox(c *ui.Context, text string, checked *bool, o CheckboxOptions) Chec k := pixel.K(c) // The base turns before it learns it is off. on := *checked - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.CheckboxBase(c, &on).Label(text).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.CheckboxBase(c, &on).Label(text).Disabled(o.Disabled) }) changed := on != *checked && !base.IsDisabled() if changed { *checked = on diff --git a/input/checkboxgroup.go b/input/checkboxgroup.go index ca7bbad..2939824 100644 --- a/input/checkboxgroup.go +++ b/input/checkboxgroup.go @@ -14,7 +14,7 @@ type CheckboxGroupOptions struct { } type CheckboxGroupResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -38,7 +38,7 @@ func CheckboxGroup(c *ui.Context, text string, items []string, checked []bool, o all := count(checked) == len(checked) && len(checked) > 0 res := CheckboxGroupResult{} res.Element = ui.Column(c).Gap(k.D(2)).Role(ui.RoleGroup).Children(func() { - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.CheckboxBase(c, &all).Label(text).Disabled(o.Disabled) }) shell.Size(k.D(12+pixel.Measure(text)), k.D(pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/input/colorslot.go b/input/colorslot.go index 6888e6a..0f31b7b 100644 --- a/input/colorslot.go +++ b/input/colorslot.go @@ -13,7 +13,7 @@ type ColorSlotOptions struct { } type ColorSlotResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/input/combobox.go b/input/combobox.go index e7acfa8..8a7b155 100644 --- a/input/combobox.go +++ b/input/combobox.go @@ -18,7 +18,7 @@ type ComboboxOptions struct { } type ComboboxResult struct { - Element *ui.Element + Element ui.Element // Changed reports a new *value; Chosen, an option taken. Changed, Chosen bool } @@ -80,7 +80,7 @@ func Combobox(c *ui.Context, text string, value *string, options []string, o Com st.open = false } show, held := st.open && len(shown) > 0, false - panel := ui.PopoverBase(c, e, &show, func(panel *ui.Element) { + panel := ui.PopoverBase(c, e, &show, func(panel ui.Element) { panel.MinWidth(e.Bounds().W) panel.HandleInput(func(ev ui.InputEvent) bool { if ev.Kind != ui.InputScroll { @@ -105,7 +105,7 @@ func Combobox(c *ui.Context, text string, value *string, options []string, o Com moved := over && [2]float32{px, py} != st.ptr st.ptr = [2]float32{px, py} menu(c, panel, func() { - items := make([]*ui.Element, len(shown)) + items := make([]ui.Element, len(shown)) for i := st.top; i < end; i++ { opt := shown[i] items[i] = ui.Row(c).Role(ui.RoleListItem).Label(opt) @@ -128,10 +128,10 @@ func Combobox(c *ui.Context, text string, value *string, options []string, o Com st.open = false } // Losing focus closes it, unless pressing on the list. - if panel != nil && panel.Pressed() { + if panel.Valid() && panel.Pressed() { held = true } - if panel != nil && st.open && !ed.Focused && !held { + if panel.Valid() && st.open && !ed.Focused && !held { st.open = false // This frame built the list: hide it, rebuild without it. panel.Invisible() diff --git a/input/dpad.go b/input/dpad.go index 03949bb..21ba79b 100644 --- a/input/dpad.go +++ b/input/dpad.go @@ -27,7 +27,7 @@ type DPadOptions struct { } type DPadResult struct { - Element *ui.Element + Element ui.Element // Pressed is the arm pressed this frame. Pressed Direction } @@ -43,7 +43,7 @@ type flash struct { // DPad is a cross pad reporting arrows and pressed arms. func DPad(c *ui.Context, text string, o DPadOptions) DPadResult { k := pixel.K(c) - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) lit := ui.Local(base, dpadKey{}, func() flash { return flash{} }) diff --git a/input/editabletext.go b/input/editabletext.go index a792042..896ed14 100644 --- a/input/editabletext.go +++ b/input/editabletext.go @@ -17,7 +17,7 @@ type EditableTextOptions struct { } type EditableTextResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -41,7 +41,7 @@ func EditableText(c *ui.Context, text string, value *string, o EditableTextOptio box.Children(func() { if s.on { ed, e := field(c, &s.draft, pixel.EditorOptions{Label: text, Placeholder: o.Placeholder}, w, nil, nil, nil) - ed.Input.AutoFocus() + pixel.AutoFocus(c, ed.Input) // Keys reach the wrapper; the editor's state gates them. on := !ed.Input.IsDisabled() enter, escape := on && pixel.Shortcut(e, 0, ui.KeyEnter), on && pixel.Shortcut(e, 0, ui.KeyEscape) @@ -54,7 +54,7 @@ func EditableText(c *ui.Context, text string, value *string, o EditableTextOptio } return } - 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.RoleButton).Label(text).Description(*value).Disabled(o.Disabled) }) if s.refocus { diff --git a/input/field.go b/input/field.go index 476af8c..377f84d 100644 --- a/input/field.go +++ b/input/field.go @@ -16,7 +16,7 @@ type FieldOptions struct { } // Field puts a label above a control, notes below. -func Field(c *ui.Context, label string, o FieldOptions, control func()) *ui.Element { +func Field(c *ui.Context, label string, o FieldOptions, control func()) ui.Element { k := pixel.K(c) return ui.Column(c).Gap(k.D(2)).Role(ui.RoleGroup).Label(label).Children(func() { pixel.Text(c, label, pixel.TextOptions{}) diff --git a/input/fieldset.go b/input/fieldset.go index bfbc7cf..bb138f4 100644 --- a/input/fieldset.go +++ b/input/fieldset.go @@ -9,7 +9,7 @@ import ( type FieldsetOptions struct{} // Fieldset frames a group of fields under a legend. -func Fieldset(c *ui.Context, legend string, o FieldsetOptions, content func()) *ui.Element { +func Fieldset(c *ui.Context, legend string, o FieldsetOptions, content func()) ui.Element { k := pixel.K(c) lw := pixel.Measure(legend) return ui.Column(c).Padding(k.D(pixel.LineHeight), k.D(8), k.D(8), k.D(8)).Gap(k.D(6)).Role(ui.RoleGroup).Label(legend).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/input/holdbutton.go b/input/holdbutton.go index 5d569e7..15f3822 100644 --- a/input/holdbutton.go +++ b/input/holdbutton.go @@ -19,7 +19,7 @@ type HoldButtonOptions struct { } type HoldButtonResult struct { - Element *ui.Element + Element ui.Element Confirmed bool } @@ -44,7 +44,7 @@ func HoldButton(c *ui.Context, text string, o HoldButtonOptions) HoldButtonResul if d == 0 { d = time.Second } - 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(k.Msg("hold")).Disabled(o.Disabled) }) h := ui.Local(base, holdButtonKey{}, func() holding { return holding{} }) diff --git a/input/joystick.go b/input/joystick.go index 53159f9..9f10bee 100644 --- a/input/joystick.go +++ b/input/joystick.go @@ -15,7 +15,7 @@ type JoystickOptions struct { } type JoystickResult struct { - Element *ui.Element + Element ui.Element // X and Y are in −1…1; Y grows downward. X, Y float64 Moved bool @@ -38,7 +38,7 @@ const ( // Joystick drags a knob in a ring; arrows nudge it. func Joystick(c *ui.Context, text string, o JoystickOptions) JoystickResult { k := pixel.K(c) - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) s := ui.Local(base, stickKey{}, func() stick { return stick{} }) diff --git a/input/knob.go b/input/knob.go index 4cecec6..7b47fc2 100644 --- a/input/knob.go +++ b/input/knob.go @@ -25,7 +25,7 @@ func Knob(c *ui.Context, text string, value *float64, o KnobOptions) KnobResult k := pixel.K(c) lo, hi := bounds(o.Min, o.Max) r := rail{lo: lo, hi: hi, step: o.Step} - 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.RoleSlider).Label(text).Description(fmtValue(*value)).Disabled(o.Disabled) }) old, set := *value, r.snap(value) diff --git a/input/multiselect.go b/input/multiselect.go index 2b4e296..bf10446 100644 --- a/input/multiselect.go +++ b/input/multiselect.go @@ -18,7 +18,7 @@ type MultiSelectOptions struct { } type MultiSelectResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -30,7 +30,7 @@ type popup struct{ open, held bool } // MultiSelect checks many options in a popup; tags show them. func MultiSelect(c *ui.Context, text string, chosen *[]string, options []string, o MultiSelectOptions) MultiSelectResult { k := pixel.K(c) - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Role(ui.RolePopUpButton).Label(text).Description(strings.Join(*chosen, ", ")).Disabled(o.Disabled) }) pop := ui.Local(base, multiKey{}, func() popup { return popup{} }) @@ -58,15 +58,15 @@ func MultiSelect(c *ui.Context, text string, chosen *[]string, options []string, }) res := MultiSelectResult{Element: shell} pop.held = false - ui.PopoverBase(c, base, open, func(panel *ui.Element) { + ui.PopoverBase(c, base, open, func(panel ui.Element) { menu(c, panel, func() { col := ui.Column(c).Gap(k.D(1)).FocusGroup(ui.Vertical) col.Children(func() { for i, opt := range options { on := slices.Contains(*chosen, opt) - item, cb, cst := pixel.Shell(c, func() *ui.Element { return ui.CheckboxBase(c, &on).Label(opt) }) + item, cb, cst := pixel.Shell(c, func() ui.Element { return ui.CheckboxBase(c, &on).Label(opt) }) if i == 0 { - cb.AutoFocus() + pixel.AutoFocus(c, cb) } // Focused, not FocusWithin: MyGo links parents after frames. pop.held = pop.held || cb.Focused() diff --git a/input/nameentry.go b/input/nameentry.go index d612718..295c1c9 100644 --- a/input/nameentry.go +++ b/input/nameentry.go @@ -14,7 +14,7 @@ type NameEntryOptions struct { } type NameEntryResult struct { - Element *ui.Element + Element ui.Element Changed, Confirmed bool } diff --git a/input/pininput.go b/input/pininput.go index f87dc89..3012f60 100644 --- a/input/pininput.go +++ b/input/pininput.go @@ -15,7 +15,7 @@ type PinInputOptions struct { } type PinInputResult struct { - Element *ui.Element + Element ui.Element // Complete reports the last box filled. Changed, Complete bool } @@ -34,7 +34,7 @@ const pinBox, pinGap = 12, 4 func PinInput(c *ui.Context, text string, value *string, o PinInputOptions) PinInputResult { k := pixel.K(c) n := pixel.Or("PinInput Length", o.Length, 4) - 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.RoleTextField).Label(text).Description(*value).Disabled(o.Disabled) }) p := ui.Local(base, pinKey{}, func() pin { return pin{} }) @@ -60,7 +60,7 @@ func PinInput(c *ui.Context, text string, value *string, o PinInputOptions) PinI put(ev.Text) // Browsers paste by command, native hosts by Cmd+V. case ev.Kind == ui.InputCommand && ev.Text == "paste", ev.Kind == ui.InputKeyDown && ev.Key == ui.KeyV && ev.Mods == ui.Cmd: - put(c.ReadClipboard()) + put(c.Services().ReadClipboard()) case ev.Kind == ui.InputKeyDown && ev.Key == ui.KeyBackspace && ev.Mods == 0: if len(*value) > 0 { *value, *changed = (*value)[:len(*value)-1], true diff --git a/input/radio.go b/input/radio.go index 46ea6be..cbfcadf 100644 --- a/input/radio.go +++ b/input/radio.go @@ -13,7 +13,7 @@ type RadioOptions struct { } type RadioResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -22,7 +22,7 @@ func Radio[T comparable](c *ui.Context, text string, value T, selected *T, o Rad k := pixel.K(c) was := *selected == value on := was - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.CheckboxBase(c, &on).Role(ui.RoleRadio).Label(text).Disabled(o.Disabled) }) res := RadioResult{Element: shell} diff --git a/input/radiogroup.go b/input/radiogroup.go index 582e3d7..9c2d1fa 100644 --- a/input/radiogroup.go +++ b/input/radiogroup.go @@ -18,14 +18,14 @@ type RadioGroupOptions struct { type radioKey struct{} type RadioGroupResult struct { - Element *ui.Element + Element ui.Element Changed bool } // RadioGroup chooses *selected; one Tab stop, arrows choose. func RadioGroup(c *ui.Context, labels []string, selected *int, o RadioGroupOptions) RadioGroupResult { k := pixel.K(c) - var e *ui.Element + var e ui.Element if o.Row { e = ui.Row(c).Gap(k.D(10)).FocusGroup(ui.Horizontal) } else { @@ -38,7 +38,7 @@ func RadioGroup(c *ui.Context, labels []string, selected *int, o RadioGroupOptio for i, l := range labels { was := *selected == i on := was - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.CheckboxBase(c, &on).Role(ui.RoleRadio).Label(l).Disabled(o.Disabled) }) if base.Focused() { diff --git a/input/rangeslider.go b/input/rangeslider.go index a856756..1ccd997 100644 --- a/input/rangeslider.go +++ b/input/rangeslider.go @@ -44,8 +44,8 @@ func RangeSlider(c *ui.Context, text string, low, high *float64, o RangeSliderOp } // knob is one draggable end, fenced by r.lo and r.hi. -func knob(c *ui.Context, k pixel.Kit, track *ui.Element, v *float64, r rail, lo, hi float64, w int) { - shell, base, st := pixel.Shell(c, func() *ui.Element { +func knob(c *ui.Context, k pixel.Kit, track ui.Element, v *float64, r rail, lo, hi float64, w int) { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.Box(c).Focusable().Role(ui.RoleSlider).Label(r.label).Description(fmtValue(*v)).Disabled(r.disabled) }) set := func(x float64) { *v = max(r.lo, min(r.hi, x)) } diff --git a/input/rating.go b/input/rating.go index eb6afa7..5497239 100644 --- a/input/rating.go +++ b/input/rating.go @@ -19,7 +19,7 @@ type RatingOptions struct { } type RatingResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -41,7 +41,7 @@ func Rating(c *ui.Context, text string, value *int, o RatingOptions) RatingResul for i := 1; i <= n; i++ { was := *value == i on := was - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.CheckboxBase(c, &on).Role(ui.RoleRadio).Label(fmt.Sprintf("%d/%d", i, n)).Disabled(o.Disabled) }) if base.Focused() { diff --git a/input/segmented.go b/input/segmented.go index 75e4951..0cb6a39 100644 --- a/input/segmented.go +++ b/input/segmented.go @@ -13,7 +13,7 @@ type SegmentedOptions struct { } type SegmentedResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -32,7 +32,7 @@ func Segmented(c *ui.Context, text string, labels []string, selected *int, o Seg g.Frame(0, 0, g.W, g.H, pixel.Notched, pixel.Face) }).Children(func() { for i, l := range labels { - shell, _, st := pixel.Shell(c, func() *ui.Element { return p.Segment(i).Label(l).Disabled(o.Disabled) }) + shell, _, st := pixel.Shell(c, func() ui.Element { return p.Segment(i).Label(l).Disabled(o.Disabled) }) shell.Size(k.D(pixel.Measure(l)+12), k.D(pixel.LineHeight+4)).Draw(func(pt *ui.Painter, r ui.Rect) { g := k.Canvas(pt, r) ink := pixel.Ink diff --git a/input/select.go b/input/select.go index 72a6ac0..72c3afb 100644 --- a/input/select.go +++ b/input/select.go @@ -15,7 +15,7 @@ type SelectOptions struct { } type SelectResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -23,12 +23,12 @@ type SelectResult struct { func Select(c *ui.Context, text string, value *string, options []string, o SelectOptions) SelectResult { k := pixel.K(c) off := o.Disabled || pixel.Current(c).Inert - var sel *ui.SelectParts[string] - var shell, base *ui.Element + var sel ui.SelectParts[string] + var shell, base ui.Element var st pixel.State // Re-keying on each off state forgets an open popup. ui.Box(c).Key(off).Children(func() { - shell, base, st = pixel.Shell(c, func() *ui.Element { + shell, base, st = pixel.Shell(c, func() ui.Element { sel = ui.SelectBase(c, value) return sel.Trigger.Label(text).Disabled(o.Disabled) }) @@ -41,7 +41,7 @@ func Select(c *ui.Context, text string, value *string, options []string, o Selec g.Finish(st) }) if !off { - sel.Popup(func(panel *ui.Element) { + sel.Popup(func(panel ui.Element) { menu(c, panel, func() { for _, opt := range options { item := sel.Item(opt).Label(opt) @@ -67,7 +67,7 @@ func widest(options []string) int { } // menu frames a popup panel as a game window. -func menu(c *ui.Context, panel *ui.Element, fn func()) { +func menu(c *ui.Context, panel ui.Element, fn func()) { k := pixel.K(c) panel.Padding(k.D(3)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) @@ -78,7 +78,7 @@ func menu(c *ui.Context, panel *ui.Element, fn func()) { } // row draws an option; a hand marks the lit one. -func row(k pixel.Kit, item *ui.Element, text string, lit, chosen bool) { +func row(k pixel.Kit, item ui.Element, text string, lit, chosen bool) { item.Height(k.D(pixel.LineHeight)).MinWidth(k.D(pixel.Measure(text) + 16)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) ink := pixel.Ink diff --git a/input/slider.go b/input/slider.go index 2a8029b..e212e5e 100644 --- a/input/slider.go +++ b/input/slider.go @@ -19,7 +19,7 @@ type SliderOptions struct { } type SliderResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -48,7 +48,7 @@ type RailOptions struct { } // SlideBase is an undrawn slider: pointer and keys set *value. -func SlideBase(c *ui.Context, text string, value *float64, o RailOptions) (shell *ui.Element, st pixel.State, changed bool) { +func SlideBase(c *ui.Context, text string, value *float64, o RailOptions) (shell ui.Element, st pixel.State, changed bool) { lo, hi := bounds(o.Min, o.Max) return slide(c, value, rail{lo: lo, hi: hi, step: o.Step, vertical: o.Vertical, label: text, disabled: o.Disabled}) } @@ -62,8 +62,8 @@ type rail struct { } // slide is a focusable track; pointer and keys set *v. -func slide(c *ui.Context, v *float64, r rail) (*ui.Element, pixel.State, bool) { - shell, base, st := pixel.Shell(c, func() *ui.Element { +func slide(c *ui.Context, v *float64, r rail) (ui.Element, pixel.State, bool) { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.Box(c).Focusable().Role(ui.RoleSlider).Label(r.label).Description(fmtValue(*v)).Disabled(r.disabled) }) old := *v @@ -102,7 +102,7 @@ func (r rail) snap(v *float64) func(float64) { } // keys step *v by arrows; Home and End jump. -func (r rail) keys(base *ui.Element, v *float64, set func(float64)) { +func (r rail) keys(base ui.Element, v *float64, set func(float64)) { step := r.step if step == 0 { step = (r.hi - r.lo) / 20 @@ -144,7 +144,7 @@ func groove(g pixel.Canvas, x, y int) { } // report logs a change and wraps the result. -func report(e *ui.Element, changed bool, kind, text string, v float64) SliderResult { +func report(e ui.Element, changed bool, kind, text string, v float64) SliderResult { if changed { slog.Debug("8bit: "+kind, "label", text, "value", v) } diff --git a/input/slotpicker.go b/input/slotpicker.go index 08cbd7c..690ecc4 100644 --- a/input/slotpicker.go +++ b/input/slotpicker.go @@ -14,7 +14,7 @@ type SlotPickerOptions struct { } type SlotPickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -33,7 +33,7 @@ func SlotPicker(c *ui.Context, text string, value *int, options []string, o Slot if *value < 0 || *value >= n { panic(fmt.Sprintf("input: SlotPicker value %d outside %d options", *value, n)) } - 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.RoleSlider).Label(text).Description(options[*value]).Disabled(o.Disabled) }) s := ui.Local(base, reelKey{}, func() reel { return reel{} }) diff --git a/input/stepper.go b/input/stepper.go index a384444..2310ce9 100644 --- a/input/stepper.go +++ b/input/stepper.go @@ -19,7 +19,7 @@ type StepperOptions struct { } type StepperResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -35,7 +35,7 @@ func Stepper(c *ui.Context, text string, value *int, o StepperOptions) StepperRe w := max(pixel.Measure(strconv.Itoa(o.Min)), pixel.Measure(strconv.Itoa(o.Max))) + 8 e := ui.Row(c).Gap(k.D(2)).AlignItems(ui.Center).Children(func() { set(*value - step*held(c, k, tinyLeft, o.Disabled)) - 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.RoleStepper).Label(text).Description(strconv.Itoa(*value)).Disabled(o.Disabled) }) if !o.Disabled { diff --git a/input/switch.go b/input/switch.go index a93b21b..2bd38c6 100644 --- a/input/switch.go +++ b/input/switch.go @@ -15,7 +15,7 @@ type SwitchOptions struct { } type SwitchResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -24,7 +24,7 @@ func Switch(c *ui.Context, text string, on *bool, o SwitchOptions) SwitchResult k := pixel.K(c) // The base turns before it learns it is off. v := *on - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.SwitchBase(c, &v).Label(label(o.Label, text)).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.SwitchBase(c, &v).Label(label(o.Label, text)).Disabled(o.Disabled) }) changed := v != *on && !base.IsDisabled() if changed { *on = v diff --git a/input/textinput.go b/input/textinput.go index 4d189ec..84c58cc 100644 --- a/input/textinput.go +++ b/input/textinput.go @@ -18,7 +18,7 @@ type TextInputOptions struct { type TextInputResult struct { // Element is laid out; Input takes the focus. - Element, Input *ui.Element + Element, Input ui.Element Changed, Submitted, Focused bool } @@ -29,7 +29,7 @@ func TextInput(c *ui.Context, label string, value *string, o TextInputOptions) T } // field frames an editor in a well with extras. -func field(c *ui.Context, value *string, eo pixel.EditorOptions, width int, bad func() bool, icon *pixel.Sprite, after func()) (pixel.Edit, *ui.Element) { +func field(c *ui.Context, value *string, eo pixel.EditorOptions, width int, bad func() bool, icon *pixel.Sprite, after func()) (pixel.Edit, ui.Element) { k := pixel.K(c) var ed pixel.Edit e := ui.Row(c).Width(k.D(pixel.Or("Width", width, 120))).Height(k.D(pixel.LineHeight+6)).Padding(0, k.D(4)).Gap(k.D(3)).AlignItems(ui.Center) @@ -69,7 +69,7 @@ func well(g pixel.Canvas, edge pixel.Slot, focus bool) { } // result wraps an editor's events and logs submits. -func result(e *ui.Element, ed pixel.Edit, label string) TextInputResult { +func result(e ui.Element, ed pixel.Edit, label string) TextInputResult { if ed.Submitted { slog.Debug("8bit: field submitted", "label", label) } diff --git a/input/toggle.go b/input/toggle.go index 5b3045b..c5df06b 100644 --- a/input/toggle.go +++ b/input/toggle.go @@ -17,7 +17,7 @@ type ToggleOptions struct { } type ToggleResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -27,7 +27,7 @@ func Toggle(c *ui.Context, text string, on *bool, o ToggleOptions) ToggleResult h, pad, top, sc := o.Size.metrics() // The base turns before it learns it is off. v := *on - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ToggleBase(c, &v).Label(label(o.Label, text)).Disabled(o.Disabled) }) changed := v != *on && !base.IsDisabled() diff --git a/input/togglegroup.go b/input/togglegroup.go index 57ef892..8959e41 100644 --- a/input/togglegroup.go +++ b/input/togglegroup.go @@ -18,7 +18,7 @@ type ToggleGroupOptions struct { } type ToggleGroupResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -34,7 +34,7 @@ func ToggleGroup(c *ui.Context, labels []string, on []bool, o ToggleGroupOptions for i, l := range labels { // The base turns before it learns it is off. v := on[i] - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ToggleBase(c, &v).Label(l).Disabled(o.Disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ToggleBase(c, &v).Label(l).Disabled(o.Disabled) }) if i > 0 { shell.Margin(0, 0, 0, k.D(-1)) } diff --git a/input/tokenfield.go b/input/tokenfield.go index 0a9e28d..6d8773a 100644 --- a/input/tokenfield.go +++ b/input/tokenfield.go @@ -19,7 +19,7 @@ type TokenFieldOptions struct { } type TokenFieldResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/input/virtualkeyboard.go b/input/virtualkeyboard.go index 68cb32a..10188f6 100644 --- a/input/virtualkeyboard.go +++ b/input/virtualkeyboard.go @@ -13,7 +13,7 @@ type VirtualKeyboardOptions struct { } type VirtualKeyboardResult struct { - Element *ui.Element + Element ui.Element Changed, Submitted bool } diff --git a/internal/archtest/archtest_test.go b/internal/archtest/archtest_test.go index 689b3dc..15aceba 100644 --- a/internal/archtest/archtest_test.go +++ b/internal/archtest/archtest_test.go @@ -166,6 +166,10 @@ func TestNoBannedCalls(t *testing.T) { if id, ok := sel.X.(*ast.Ident); sel.Sel.Name == "Shortcut" && !pkg && !(ok && id.Name == "c") { t.Errorf("%s: %s.Shortcut skips the enabled check; use pixel.Shortcut", f.path, receiver(sel.X)) } + // It moves the focus before an overlay notes its opener. + if sel.Sel.Name == "AutoFocus" && !pkg { + t.Errorf("%s: %s.AutoFocus loses an overlay's opener; use pixel.AutoFocus", f.path, receiver(sel.X)) + } } return true }) diff --git a/layout/appframe.go b/layout/appframe.go index 47988d5..e5c614c 100644 --- a/layout/appframe.go +++ b/layout/appframe.go @@ -14,7 +14,7 @@ type AppFrameOptions struct { } // AppFrame arranges header, sidebar, main and footer; nil ones vanish. -func AppFrame(c *ui.Context, o AppFrameOptions, header, sidebar, main, footer func()) *ui.Element { +func AppFrame(c *ui.Context, o AppFrameOptions, header, sidebar, main, footer func()) ui.Element { k := pixel.K(c) sw := pixel.Or("AppFrame SidebarWidth", o.SidebarWidth, 96) fold := pixel.Or("AppFrame FoldBelow", o.FoldBelow, 320) @@ -43,7 +43,7 @@ func AppFrame(c *ui.Context, o AppFrameOptions, header, sidebar, main, footer fu if header != nil { strip(header, true) } - var body *ui.Element + var body ui.Element if folded { body = ui.Column(c) } else { diff --git a/layout/aspectbox.go b/layout/aspectbox.go index d9489b9..1c02ec7 100644 --- a/layout/aspectbox.go +++ b/layout/aspectbox.go @@ -12,7 +12,7 @@ type AspectBoxOptions struct { } // AspectBox fills the width and keeps W:H in whole dots. -func AspectBox(c *ui.Context, o AspectBoxOptions, children func()) *ui.Element { +func AspectBox(c *ui.Context, o AspectBoxOptions, children func()) ui.Element { k := pixel.K(c) rw, rh := pixel.Or("AspectBox W", o.W, 1), pixel.Or("AspectBox H", o.H, 1) e := ui.Box(c).FillWidth().Clip() diff --git a/layout/card.go b/layout/card.go index 7ffcd15..67a594b 100644 --- a/layout/card.go +++ b/layout/card.go @@ -14,7 +14,7 @@ type CardOptions struct { } // Card is a raised panel: header strip, body, footer. -func Card(c *ui.Context, o CardOptions, body func()) *ui.Element { +func Card(c *ui.Context, o CardOptions, body func()) ui.Element { k := pixel.K(c) e := ui.Column(c).Padding(k.D(2)).Draw(func(p *ui.Painter, r ui.Rect) { box(k.Canvas(p, r), pixel.Raised, pixel.Face) diff --git a/layout/crtframe.go b/layout/crtframe.go index eec9d80..66354f3 100644 --- a/layout/crtframe.go +++ b/layout/crtframe.go @@ -15,7 +15,7 @@ type CRTFrameOptions struct { const crtSide, crtChin = 10, 16 // CRTFrame shows children on a monitor with scanlines. -func CRTFrame(c *ui.Context, o CRTFrameOptions, children func()) *ui.Element { +func CRTFrame(c *ui.Context, o CRTFrameOptions, children func()) ui.Element { k := pixel.K(c) return ui.Column(c).Padding(k.D(crtSide), k.D(crtSide), k.D(crtChin), k.D(crtSide)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/layout/divider.go b/layout/divider.go index f2e281e..bb636b2 100644 --- a/layout/divider.go +++ b/layout/divider.go @@ -34,7 +34,7 @@ var diamond = pixel.MustSprite(` `) // Divider is a rule across its parent. -func Divider(c *ui.Context, o DividerOptions) *ui.Element { +func Divider(c *ui.Context, o DividerOptions) ui.Element { k := pixel.K(c) ink := o.Slot if ink == pixel.Ink { diff --git a/layout/grid.go b/layout/grid.go index b094cb0..885ba23 100644 --- a/layout/grid.go +++ b/layout/grid.go @@ -25,7 +25,7 @@ type GridOptions struct { } // Grid lays n cells in equal columns, lines between them. -func Grid(c *ui.Context, n int, o GridOptions, cell func(i int)) *ui.Element { +func Grid(c *ui.Context, n int, o GridOptions, cell func(i int)) ui.Element { k := pixel.K(c) cols := pixel.Or("Grid Columns", o.Columns, 2) pad := pixel.Or("Grid Pad", o.Pad, 4) diff --git a/layout/handheld.go b/layout/handheld.go index 507ff29..5b28141 100644 --- a/layout/handheld.go +++ b/layout/handheld.go @@ -18,7 +18,7 @@ type HandheldOptions struct { } type HandheldResult struct { - Element *ui.Element + Element ui.Element Up, Down, Left, Right, A, B bool } @@ -69,7 +69,7 @@ func Handheld(c *ui.Context, o HandheldOptions, screen func()) HandheldResult { // key is a d-pad arm, or a round button. func key(c *ui.Context, k pixel.Kit, name string, x, y, w, h int, round, disabled bool) bool { - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(name).Disabled(disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(name).Disabled(disabled) }) shell.Absolute().Left(k.D(x)).Top(k.D(y)).Size(k.D(w), k.D(h)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) dy := 0 diff --git a/layout/layers.go b/layout/layers.go index 579937e..c982992 100644 --- a/layout/layers.go +++ b/layout/layers.go @@ -5,7 +5,7 @@ import "github.com/egoist/mygo/ui" type LayersOptions struct{} // Layers stacks layers in a box; the first sizes it. -func Layers(c *ui.Context, o LayersOptions, layers ...func()) *ui.Element { +func Layers(c *ui.Context, o LayersOptions, layers ...func()) ui.Element { if len(layers) == 0 { panic("layout: Layers needs a layer") } diff --git a/layout/layout.go b/layout/layout.go index 23f5e5a..3c3259c 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -29,7 +29,7 @@ func rule(g pixel.Canvas, x, y, n int, vertical bool, on, off int, s pixel.Slot) type dragKey struct{} // drag is enabled e's pointer travel this frame, in dots. -func drag(k pixel.Kit, e *ui.Element) (dx, dy int, ok bool) { +func drag(k pixel.Kit, e ui.Element) (dx, dy int, ok bool) { rest := ui.Local(e, dragKey{}, func() [2]float32 { return [2]float32{} }) fx, fy, ok := e.Dragged() if !ok || e.IsDisabled() { diff --git a/layout/layout_test.go b/layout/layout_test.go index e9195b8..fc4717c 100644 --- a/layout/layout_test.go +++ b/layout/layout_test.go @@ -302,44 +302,48 @@ func TestAppFrameFolds(t *testing.T) { } func TestAspectBoxAndViewport(t *testing.T) { - var aspect, screen *ui.Element + var aspect, screen ui.Handle + var ctx *ui.Context tt := ui.NewTester(func(c *ui.Context) { + ctx = c use(c) ui.Column(c).Children(func() { ui.Box(c).Width(300).Children(func() { - aspect = layout.AspectBox(c, layout.AspectBoxOptions{W: 16, H: 9}, func() {}) + layout.AspectBox(c, layout.AspectBoxOptions{W: 16, H: 9}, func() {}).Bind(&aspect) }) ui.Box(c).Size(400, 300).Children(func() { - layout.Viewport(c, layout.ViewportOptions{W: 64, H: 40}, func() { screen = ui.Box(c).Fill() }) + layout.Viewport(c, layout.ViewportOptions{W: 64, H: 40}, func() { ui.Box(c).Fill().Bind(&screen) }) }) }) }, 500, 700) tt.Frame() - if b := aspect.Bounds(); b.W != 300 || b.H != 2*(150*9/16) { + if b := aspect.Bounds(ctx); b.W != 300 || b.H != 2*(150*9/16) { t.Fatalf("aspect box %v", b) } - if b := screen.Bounds(); b.X != 72 || b.Y != aspect.Bounds().H+70 || b.W != 256 || b.H != 160 { + if b := screen.Bounds(ctx); b.X != 72 || b.Y != aspect.Bounds(ctx).H+70 || b.W != 256 || b.H != 160 { t.Fatalf("viewport screen %v", b) } } func TestLayersSizes(t *testing.T) { - var auto, sized, fill *ui.Element + var auto, sized, fill ui.Handle + var ctx *ui.Context tt := ui.NewTester(func(c *ui.Context) { + ctx = c use(c) ui.Column(c).AlignItems(ui.Start).Children(func() { - auto = layout.Layers(c, layout.LayersOptions{}, func() { ui.Box(c).Size(50, 30) }, func() { ui.Box(c).Fill() }) - sized = layout.Layers(c, layout.LayersOptions{}, func() { fill = ui.Box(c).Fill() }).Size(120, 64) + layout.Layers(c, layout.LayersOptions{}, func() { ui.Box(c).Size(50, 30) }, func() { ui.Box(c).Fill() }).Bind(&auto) + layout.Layers(c, layout.LayersOptions{}, func() { ui.Box(c).Fill().Bind(&fill) }).Size(120, 64).Bind(&sized) }) }, 400, 300) tt.Frame() - if b := auto.Bounds(); b.W != 50 || b.H != 30 { + if b := auto.Bounds(ctx); b.W != 50 || b.H != 30 { t.Fatalf("first layer did not size the box: %v", b) } - if b := fill.Bounds(); b.W != 120 || b.H != 64 { + if b := fill.Bounds(ctx); b.W != 120 || b.H != 64 { t.Fatalf("first layer did not fill a sized box: %v", b) } - if b := sized.Bounds(); b.H != 64 { + if b := sized.Bounds(ctx); b.H != 64 { t.Fatalf("sized box %v", b) } } diff --git a/layout/masonry.go b/layout/masonry.go index b3d6361..fbbe616 100644 --- a/layout/masonry.go +++ b/layout/masonry.go @@ -16,7 +16,7 @@ type MasonryOptions struct { } // Masonry puts each item into the shortest column. -func Masonry(c *ui.Context, heights []int, o MasonryOptions, item func(i int)) *ui.Element { +func Masonry(c *ui.Context, heights []int, o MasonryOptions, item func(i int)) ui.Element { k := pixel.K(c) gap := pixel.Or("Masonry Gap", o.Gap, 4) n := pixel.Or("Masonry Columns", o.Columns, 3) diff --git a/layout/panel.go b/layout/panel.go index d4d75ba..760190f 100644 --- a/layout/panel.go +++ b/layout/panel.go @@ -15,7 +15,7 @@ type PanelOptions struct { } // Panel frames children in a style. -func Panel(c *ui.Context, o PanelOptions, children func()) *ui.Element { +func Panel(c *ui.Context, o PanelOptions, children func()) ui.Element { k := pixel.K(c) fill := o.Fill if fill == pixel.Ink { diff --git a/layout/resizable.go b/layout/resizable.go index c13cdb6..bca257b 100644 --- a/layout/resizable.go +++ b/layout/resizable.go @@ -19,7 +19,7 @@ type ResizableOptions struct { } type ResizableResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -41,7 +41,7 @@ func Resizable(c *ui.Context, w, h *int, o ResizableOptions, children func()) Re e := ui.Box(c).Clip() e.Children(func() { children() - 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.RoleSplitter).Label(label(k, o.Label, "resize")).Disabled(o.Disabled).Cursor(ui.CursorResizeNWSE) }) if dx, dy, ok := drag(k, base); ok { diff --git a/layout/screen.go b/layout/screen.go index cc92950..3f0557a 100644 --- a/layout/screen.go +++ b/layout/screen.go @@ -13,7 +13,7 @@ type ScreenOptions struct { } // Screen fills the window with Back; children form a column. -func Screen(c *ui.Context, o ScreenOptions, children func()) *ui.Element { +func Screen(c *ui.Context, o ScreenOptions, children func()) ui.Element { k := pixel.K(c) pad := pixel.Or("Screen Padding", o.Padding, 0) return ui.Column(c).Fill().Padding(k.D(pad)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/layout/scrollarea.go b/layout/scrollarea.go index 135bd38..11beb64 100644 --- a/layout/scrollarea.go +++ b/layout/scrollarea.go @@ -32,7 +32,7 @@ type ScrollAreaOptions struct { } type ScrollAreaResult struct { - Element *ui.Element + Element ui.Element // Changed reports the view moved since the last frame. Changed bool } @@ -66,7 +66,7 @@ func ScrollArea(c *ui.Context, s *ui.ScrollState, o ScrollAreaOptions, children } st.end = s.MaxY } - var v *ui.Element + var v ui.Element row.Children(func() { scrollBar(c, k, s, view, o.Disabled || inert, o.Disabled) // A disabled view is a new element; the focus leaves. @@ -161,7 +161,7 @@ func thumb(h, view, y, maxY, dot float32) (size, at float32) { // arrow is a scrollbar button; keys scroll the view instead. func arrow(c *ui.Context, k pixel.Kit, sp *pixel.Sprite, label string, disabled bool) bool { - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.Box(c).Role(ui.RoleButton).Label(label).Disabled(disabled) }) shell.Size(k.D(barW), k.D(barW)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/layout/section.go b/layout/section.go index 733ed81..2a679f8 100644 --- a/layout/section.go +++ b/layout/section.go @@ -14,7 +14,7 @@ type SectionOptions struct { } // Section titles a group inside a dotted rule. -func Section(c *ui.Context, title string, o SectionOptions, children func()) *ui.Element { +func Section(c *ui.Context, title string, o SectionOptions, children func()) ui.Element { k := pixel.K(c) up := strings.ToUpper(title) return ui.Column(c).Gap(k.D(pixel.Or("Section Gap", o.Gap, 4))).Role(ui.RoleGroup).Label(title).Children(func() { diff --git a/layout/split.go b/layout/split.go index 4baee5d..129348a 100644 --- a/layout/split.go +++ b/layout/split.go @@ -20,7 +20,7 @@ type SplitOptions struct { } type SplitResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -28,7 +28,7 @@ type SplitResult struct { func Split(c *ui.Context, size *int, o SplitOptions, first, second func()) SplitResult { k := pixel.K(c) least := pixel.Or("Split Min", o.Min, 24) - var e *ui.Element + var e ui.Element var total float32 back, fwd := ui.KeyLeft, ui.KeyRight if o.Vertical { @@ -47,7 +47,7 @@ func Split(c *ui.Context, size *int, o SplitOptions, first, second func()) Split start := *size e.AlignItems(ui.Stretch).Children(func() { pane := ui.Box(c).Clip().Children(first) - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { cur := ui.CursorResizeEW if o.Vertical { cur = ui.CursorResizeNS diff --git a/layout/statusbar.go b/layout/statusbar.go index 37773e2..675db08 100644 --- a/layout/statusbar.go +++ b/layout/statusbar.go @@ -9,7 +9,7 @@ import ( type StatusBarOptions struct{} // StatusBar shows segments split by sunken dividers; the first grows. -func StatusBar(c *ui.Context, segments []string, o StatusBarOptions) *ui.Element { +func StatusBar(c *ui.Context, segments []string, o StatusBarOptions) ui.Element { k := pixel.K(c) return ui.Row(c).FillWidth().Height(k.D(pixel.LineHeight + 3)).AlignItems(ui.Stretch).Role(ui.RoleStatus).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/layout/tiles.go b/layout/tiles.go index 1250a92..0162922 100644 --- a/layout/tiles.go +++ b/layout/tiles.go @@ -14,7 +14,7 @@ type TilesOptions struct { } // Tiles wraps n equal tiles into rows. -func Tiles(c *ui.Context, n int, o TilesOptions, tile func(i int)) *ui.Element { +func Tiles(c *ui.Context, n int, o TilesOptions, tile func(i int)) ui.Element { k := pixel.K(c) w := pixel.Or("Tiles W", o.W, 48) h := pixel.Or("Tiles H", o.H, w) diff --git a/layout/titlebar.go b/layout/titlebar.go index 3792d4b..6451224 100644 --- a/layout/titlebar.go +++ b/layout/titlebar.go @@ -32,7 +32,7 @@ type TitleBarOptions struct { } type TitleBarResult struct { - Element *ui.Element + Element ui.Element Close bool Minimize bool } @@ -67,7 +67,7 @@ func TitleBar(c *ui.Context, title string, o TitleBarOptions) TitleBarResult { // windowBox is a framed button showing its glyph on hover. func windowBox(c *ui.Context, k pixel.Kit, glyph *pixel.Sprite, name string, disabled bool) bool { - shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(name).Disabled(disabled) }) + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(name).Disabled(disabled) }) shell.Size(k.D(11), k.D(11)).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) fill := pixel.Face diff --git a/layout/viewport.go b/layout/viewport.go index 5e2ab10..a7ac2f8 100644 --- a/layout/viewport.go +++ b/layout/viewport.go @@ -12,7 +12,7 @@ type ViewportOptions struct { } // Viewport fills its parent and draws children at W×H dots. -func Viewport(c *ui.Context, o ViewportOptions, children func()) *ui.Element { +func Viewport(c *ui.Context, o ViewportOptions, children func()) ui.Element { k := pixel.K(c) vw, vh := pixel.Or("Viewport W", o.W, 160), pixel.Or("Viewport H", o.H, 144) base := int(k.Dot()) diff --git a/layout/window.go b/layout/window.go index 5ea44a9..df40c7a 100644 --- a/layout/window.go +++ b/layout/window.go @@ -16,7 +16,7 @@ type WindowOptions struct { } // Window is a beveled window with an optional title tab. -func Window(c *ui.Context, o WindowOptions, children func()) *ui.Element { +func Window(c *ui.Context, o WindowOptions, children func()) ui.Element { k := pixel.K(c) pad := 3 + pixel.Or("Window Padding", o.Padding, 6) top := 0 diff --git a/media/audioplayer.go b/media/audioplayer.go index 0f5688d..5dfc7a0 100644 --- a/media/audioplayer.go +++ b/media/audioplayer.go @@ -24,7 +24,7 @@ type AudioPlayerOptions struct { } type AudioPlayerResult struct { - Element *ui.Element + Element ui.Element // Changed is play, seek or volume; others are presses. Changed, Stopped, Previous, Next bool } diff --git a/media/cartridge.go b/media/cartridge.go index c59efb4..bcd58ea 100644 --- a/media/cartridge.go +++ b/media/cartridge.go @@ -17,7 +17,7 @@ type CartridgeOptions struct { } // Cartridge is a game cart with a sticker: art, title. -func Cartridge(c *ui.Context, text string, o CartridgeOptions) *ui.Element { +func Cartridge(c *ui.Context, text string, o CartridgeOptions) ui.Element { k := pixel.K(c) if o.Art != nil && (o.Art.W > 38 || o.Art.H > 22) { panic("media: Cartridge Art is larger than 38 by 22") diff --git a/media/cassette.go b/media/cassette.go index 35ceb9b..b40244b 100644 --- a/media/cassette.go +++ b/media/cassette.go @@ -19,7 +19,7 @@ type CassetteOptions struct { } // Cassette turns its reels while playing; tape winds right. -func Cassette(c *ui.Context, text string, o CassetteOptions) *ui.Element { +func Cassette(c *ui.Context, text string, o CassetteOptions) ui.Element { k := pixel.K(c) if !(o.Progress >= 0 && o.Progress <= 1) { panic(fmt.Sprintf("media: Cassette Progress %v outside 0–1", o.Progress)) diff --git a/media/equalizer.go b/media/equalizer.go index 4affb09..7876a7c 100644 --- a/media/equalizer.go +++ b/media/equalizer.go @@ -17,7 +17,7 @@ type EqualizerOptions struct { } type EqualizerResult struct { - Element *ui.Element + Element ui.Element // Band is the band changed this frame, else -1. Band int } diff --git a/media/filmstrip.go b/media/filmstrip.go index 8b2c7a1..31679c8 100644 --- a/media/filmstrip.go +++ b/media/filmstrip.go @@ -12,7 +12,7 @@ type FilmstripOptions struct { } // Filmstrip lays a sprite's frames on film with sprocket holes. -func Filmstrip(c *ui.Context, text string, sp *pixel.Sprite, o FilmstripOptions) *ui.Element { +func Filmstrip(c *ui.Context, text string, sp *pixel.Sprite, o FilmstripOptions) ui.Element { k := pixel.K(c) s := scale("Filmstrip", o.Scale, 2) n := sp.Frames() diff --git a/media/gallery.go b/media/gallery.go index 8867d0f..bcbba14 100644 --- a/media/gallery.go +++ b/media/gallery.go @@ -16,7 +16,7 @@ type GalleryOptions struct { } type GalleryResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/imagecompare.go b/media/imagecompare.go index e50ca0e..c5fc303 100644 --- a/media/imagecompare.go +++ b/media/imagecompare.go @@ -18,7 +18,7 @@ type ImageCompareOptions struct { } type ImageCompareResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/media.go b/media/media.go index 11b70ef..26406b8 100644 --- a/media/media.go +++ b/media/media.go @@ -39,7 +39,7 @@ func scale(name string, s, def int) int { } // art is a named picture w by h dots. -func art(c *ui.Context, k pixel.Kit, label string, w, h int) *ui.Element { +func art(c *ui.Context, k pixel.Kit, label string, w, h int) ui.Element { if label == "" { panic("media: a picture needs a name") } @@ -75,7 +75,7 @@ type turn struct { } // turned counts steps while playing; stopped, it holds still. -func turned(c *ui.Context, k pixel.Kit, e *ui.Element, playing bool, fps int) int { +func turned(c *ui.Context, k pixel.Kit, e ui.Element, playing bool, fps int) int { s := ui.Local(e, turnKey{}, func() turn { return turn{} }) on := playing && !k.Reduce() if on { diff --git a/media/notes.go b/media/notes.go index 861fef0..e9567a0 100644 --- a/media/notes.go +++ b/media/notes.go @@ -25,7 +25,7 @@ func note(semi int) string { } // played are the semitones whose letters e heard this frame. -func played(e *ui.Element) []int { +func played(e ui.Element) []int { var out []int for semi, kb := range keyboard { if pixel.Shortcut(e, 0, kb.key) { diff --git a/media/pianokeys.go b/media/pianokeys.go index 6e082e4..4fb81ed 100644 --- a/media/pianokeys.go +++ b/media/pianokeys.go @@ -14,7 +14,7 @@ type PianoKeysOptions struct { } type PianoKeysResult struct { - Element *ui.Element + Element ui.Element // Struck are notes begun this frame, like "C#4". Struck []string } @@ -36,7 +36,7 @@ var ( // PianoKeys plays C4–C5 by pointer, A–K and WETYU. func PianoKeys(c *ui.Context, text string, o PianoKeysOptions) PianoKeysResult { k := pixel.K(c) - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) hd := ui.Local(base, pianoKey{}, func() hand { return hand{held: -1, typed: -1} }) diff --git a/media/playlist.go b/media/playlist.go index dfe492b..009dc51 100644 --- a/media/playlist.go +++ b/media/playlist.go @@ -17,7 +17,7 @@ type PlaylistOptions struct { } type PlaylistResult struct { - Element *ui.Element + Element ui.Element // Submitted is Enter or a double click on a row. Changed, Submitted bool } diff --git a/media/recordbutton.go b/media/recordbutton.go index 97ad048..61dbb45 100644 --- a/media/recordbutton.go +++ b/media/recordbutton.go @@ -15,7 +15,7 @@ type RecordButtonOptions struct { } type RecordButtonResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/scrubber.go b/media/scrubber.go index 307ff81..ad73b22 100644 --- a/media/scrubber.go +++ b/media/scrubber.go @@ -18,7 +18,7 @@ type ScrubberOptions struct { } type ScrubberResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/stepsequencer.go b/media/stepsequencer.go index 33be927..5714080 100644 --- a/media/stepsequencer.go +++ b/media/stepsequencer.go @@ -18,7 +18,7 @@ type StepSequencerOptions struct { } type StepSequencerResult struct { - Element *ui.Element + Element ui.Element Changed bool // Step is the playhead, 0–15, or -1 when stopped. Step int diff --git a/media/tracker.go b/media/tracker.go index 69f1de0..102036f 100644 --- a/media/tracker.go +++ b/media/tracker.go @@ -23,7 +23,7 @@ type TrackerOptions struct { } type TrackerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/transport.go b/media/transport.go index eb6a80a..99a858e 100644 --- a/media/transport.go +++ b/media/transport.go @@ -15,7 +15,7 @@ type TransportOptions struct { } type TransportResult struct { - Element *ui.Element + Element ui.Element // Changed is play or pause; the rest are presses. Changed, Stopped, Previous, Next bool } diff --git a/media/tuner.go b/media/tuner.go index e24fbd8..540ddc3 100644 --- a/media/tuner.go +++ b/media/tuner.go @@ -15,7 +15,7 @@ type TunerOptions struct { } type TunerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/videoframe.go b/media/videoframe.go index 972135b..b46cd15 100644 --- a/media/videoframe.go +++ b/media/videoframe.go @@ -19,7 +19,7 @@ type VideoFrameOptions struct { } type VideoFrameResult struct { - Element *ui.Element + Element ui.Element // Changed is play, pause or a seek. Changed bool } diff --git a/media/vinyl.go b/media/vinyl.go index 3d49784..c3fa21a 100644 --- a/media/vinyl.go +++ b/media/vinyl.go @@ -13,7 +13,7 @@ type VinylOptions struct { } // Vinyl turns a record while playing; the arm swings on. -func Vinyl(c *ui.Context, text string, o VinylOptions) *ui.Element { +func Vinyl(c *ui.Context, text string, o VinylOptions) ui.Element { k := pixel.K(c) e := art(c, k, text, 72, 64) swing := float64(k.Toward(c, e, o.Playing, 12, 6)) / 6 diff --git a/media/visualizer.go b/media/visualizer.go index 354f64a..1fa5b79 100644 --- a/media/visualizer.go +++ b/media/visualizer.go @@ -16,7 +16,7 @@ type VisualizerOptions struct { } // Visualizer mirrors each level, 0 to 1, about the middle. -func Visualizer(c *ui.Context, text string, levels []float64, o VisualizerOptions) *ui.Element { +func Visualizer(c *ui.Context, text string, levels []float64, o VisualizerOptions) ui.Element { k := pixel.K(c) if len(levels) == 0 { panic("media: Visualizer has no levels") diff --git a/media/volume.go b/media/volume.go index cd1fe6f..e545757 100644 --- a/media/volume.go +++ b/media/volume.go @@ -18,7 +18,7 @@ type VolumeOptions struct { } type VolumeResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/media/waveform.go b/media/waveform.go index ff1d811..00da089 100644 --- a/media/waveform.go +++ b/media/waveform.go @@ -15,7 +15,7 @@ type WaveformOptions struct { } // Waveform draws samples, ±1, as lines from the middle. -func Waveform(c *ui.Context, text string, samples []float64, o WaveformOptions) *ui.Element { +func Waveform(c *ui.Context, text string, samples []float64, o WaveformOptions) ui.Element { k := pixel.K(c) if len(samples) == 0 { panic("media: Waveform has no samples") diff --git a/navigation/backforward.go b/navigation/backforward.go index 6c8e392..91b863d 100644 --- a/navigation/backforward.go +++ b/navigation/backforward.go @@ -15,7 +15,7 @@ type BackForwardOptions struct { } type BackForwardResult struct { - Element *ui.Element + Element ui.Element // Moved reports a step through the history. Moved bool } diff --git a/navigation/bottomnav.go b/navigation/bottomnav.go index 1d49930..23452a6 100644 --- a/navigation/bottomnav.go +++ b/navigation/bottomnav.go @@ -13,7 +13,7 @@ type BottomNavOptions struct { } type BottomNavResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -25,7 +25,7 @@ func BottomNav(c *ui.Context, text string, items []NavItem, selected *int, o Bot v := *selected res.Element = ui.RadioGroup(c, func() { for i, it := range items { - shell, _, st := pixel.Shell(c, func() *ui.Element { + shell, _, st := pixel.Shell(c, func() ui.Element { return ui.RadioBase(c, &v, i).Label(it.Label).Disabled(o.Disabled) }) w := max(pixel.Measure(it.Label), 12) + 12 diff --git a/navigation/breadcrumbs.go b/navigation/breadcrumbs.go index 8f7af81..883f378 100644 --- a/navigation/breadcrumbs.go +++ b/navigation/breadcrumbs.go @@ -13,7 +13,7 @@ type BreadcrumbsOptions struct { } type BreadcrumbsResult struct { - Element *ui.Element + Element ui.Element // Clicked is the part clicked, or -1. Clicked int } @@ -28,7 +28,7 @@ func Breadcrumbs(c *ui.Context, text string, parts []string, o BreadcrumbsOption pixel.Text(c, part, pixel.TextOptions{}) break } - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Role(ui.RoleLink).Label(part).Disabled(o.Disabled) }) if base.Clicked() { diff --git a/navigation/commandmenu.go b/navigation/commandmenu.go index b674f31..1ab85da 100644 --- a/navigation/commandmenu.go +++ b/navigation/commandmenu.go @@ -15,7 +15,7 @@ type CommandMenuOptions struct { } type CommandMenuResult struct { - Element *ui.Element + Element ui.Element // Chosen is the item taken this frame, or -1. Chosen int Canceled bool @@ -26,12 +26,12 @@ type cursorKey struct{} // CommandMenu is a JRPG command window with a bobbing ▶. func CommandMenu(c *ui.Context, text string, items []MenuItem, o CommandMenuOptions) CommandMenuResult { k := pixel.K(c) - 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.RoleList).Label(text).Disabled(o.Disabled) }) // An off menu never takes the focus. if o.AutoFocus && !base.IsDisabled() { - base.AutoFocus() + pixel.AutoFocus(c, base) } cur := ui.Local(base, cursorKey{}, func() int { return 0 }) res := CommandMenuResult{Element: shell, Chosen: -1} diff --git a/navigation/commandpalette.go b/navigation/commandpalette.go index c1e7fec..e080ced 100644 --- a/navigation/commandpalette.go +++ b/navigation/commandpalette.go @@ -15,7 +15,7 @@ type CommandPaletteOptions struct { } type CommandPaletteResult struct { - Element *ui.Element + Element ui.Element // Ran is the command run this frame, or -1. Ran int } @@ -54,7 +54,7 @@ func CommandPalette(c *ui.Context, text string, commands []MenuItem, open *bool, res.Ran, *open = i, false } } - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { back.Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) g.Checker(0, 0, g.W, g.H, pixel.Back) @@ -64,7 +64,7 @@ func CommandPalette(c *ui.Context, text string, commands []MenuItem, open *bool, g.Frame(0, 0, g.W, g.H, pixel.Window, pixel.Face) }).Children(func() { f := input.TextInput(c, text, &st.query, input.TextInputOptions{Placeholder: "> …", Width: 228}) - f.Input.AutoFocus() + pixel.AutoFocus(c, f.Input) if f.Changed { st.lit = 0 } diff --git a/navigation/contextmenu.go b/navigation/contextmenu.go index 103d9fe..7e71340 100644 --- a/navigation/contextmenu.go +++ b/navigation/contextmenu.go @@ -13,7 +13,7 @@ type ContextMenuOptions struct { } type ContextMenuResult struct { - Element *ui.Element + Element ui.Element // Chosen is the item taken this frame, or -1. Chosen int } @@ -70,7 +70,7 @@ func ContextMenu(c *ui.Context, text string, items []MenuItem, o ContextMenuOpti if clicked >= 0 { res.Chosen = clicked } - if panel != nil { + if panel.Valid() { b := area.Bounds() panel.Left(b.X + s.x).Top(b.Y + s.y) } diff --git a/navigation/documenttabs.go b/navigation/documenttabs.go index 2046798..f588f55 100644 --- a/navigation/documenttabs.go +++ b/navigation/documenttabs.go @@ -22,7 +22,7 @@ type DocumentTabsOptions struct { } type DocumentTabsResult struct { - Element *ui.Element + Element ui.Element Changed bool // Closed is the tab × or Delete closed, or -1. Closed int @@ -46,8 +46,8 @@ func DocumentTabs(c *ui.Context, text string, titles []string, selected *int, o for i, t := range titles { // Keyed by title, so focus follows its tab. ui.Box(c).Key(t).Children(func() { - var x *ui.Element - shell, base, st := pixel.Shell(c, func() *ui.Element { + var x ui.Element + shell, base, st := pixel.Shell(c, func() ui.Element { tab := p.Tab(i).Label(t).Disabled(o.Disabled) // The × is no Tab stop; Delete closes by keyboard. tab.Children(func() { diff --git a/navigation/dropdown.go b/navigation/dropdown.go index 830f135..44e1d34 100644 --- a/navigation/dropdown.go +++ b/navigation/dropdown.go @@ -14,7 +14,7 @@ type DropdownMenuOptions struct { } type DropdownMenuResult struct { - Element *ui.Element + Element ui.Element // Chosen is the item taken this frame, or -1. Chosen int } diff --git a/navigation/levelselect.go b/navigation/levelselect.go index ab67a2f..8e54be3 100644 --- a/navigation/levelselect.go +++ b/navigation/levelselect.go @@ -34,7 +34,7 @@ type LevelSelectOptions struct { } type LevelSelectResult struct { - Element *ui.Element + Element ui.Element // Chosen is Enter or a click on an open stage. Changed, Chosen bool } diff --git a/navigation/menubar.go b/navigation/menubar.go index 30747fa..38886ca 100644 --- a/navigation/menubar.go +++ b/navigation/menubar.go @@ -19,7 +19,7 @@ type MenuBarOptions struct { } type MenuBarResult struct { - Element *ui.Element + Element ui.Element // Menu and Item are this frame's choice, else -1. Menu, Item int } @@ -66,7 +66,7 @@ func MenuBar(c *ui.Context, text string, menus []Menu, o MenuBarOptions) MenuBar px, py, over := row.PointerPosition() moved := over && [2]float32{px, py} != b.ptr b.ptr = [2]float32{px, py} - var anchor *ui.Element + var anchor ui.Element row.Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) g.Rect(0, 0, g.W, g.H, pixel.Face) @@ -92,7 +92,7 @@ func MenuBar(c *ui.Context, text string, menus []Menu, o MenuBarOptions) MenuBar } } }) - if b.open >= 0 && anchor != nil { + if b.open >= 0 && anchor.Valid() { items := menus[b.open].Items res.Item = b.m.keys(row.Shortcut, items) if _, clicked := b.m.list(c, anchor, items); clicked >= 0 { diff --git a/navigation/menus.go b/navigation/menus.go index 0585292..7cdd6cc 100644 --- a/navigation/menus.go +++ b/navigation/menus.go @@ -62,10 +62,10 @@ func (m *menu) keys(short func(ui.Modifiers, ui.Key) bool, items []MenuItem) int } // list shows the menu under anchor and returns clicks. -func (m *menu) list(c *ui.Context, anchor *ui.Element, items []MenuItem) (panel *ui.Element, clicked int) { +func (m *menu) list(c *ui.Context, anchor ui.Element, items []MenuItem) (panel ui.Element, clicked int) { k := pixel.K(c) clicked = -1 - panel = ui.PopoverBase(c, anchor, &m.open, func(panel *ui.Element) { + panel = ui.PopoverBase(c, anchor, &m.open, func(panel ui.Element) { // Only a pointer that moves lights the item under it. px, py, over := panel.PointerPosition() moved := over && [2]float32{px, py} != m.ptr @@ -104,7 +104,7 @@ func (m *menu) list(c *ui.Context, anchor *ui.Element, items []MenuItem) (panel }) }) // Clicks on a rule or the edge close it too. - if panel != nil && panel.Clicked() { + if panel.Valid() && panel.Clicked() { m.open = false } return panel, clicked diff --git a/navigation/pagedots.go b/navigation/pagedots.go index 67e4886..6ca3fe3 100644 --- a/navigation/pagedots.go +++ b/navigation/pagedots.go @@ -14,7 +14,7 @@ type PageDotsOptions struct { } type PageDotsResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -22,7 +22,7 @@ type PageDotsResult struct { func PageDots(c *ui.Context, text string, page *int, total int, o PageDotsOptions) PageDotsResult { k := pixel.K(c) check(*page, total) - 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.RoleSlider).Label(text).Description(fmt.Sprintf("%d/%d", *page, total)).Disabled(o.Disabled) }) old := *page diff --git a/navigation/pagination.go b/navigation/pagination.go index f711114..9c9d7a6 100644 --- a/navigation/pagination.go +++ b/navigation/pagination.go @@ -17,7 +17,7 @@ type PaginationOptions struct { } type PaginationResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/navigation/rail.go b/navigation/rail.go index ae4488d..0ad24a9 100644 --- a/navigation/rail.go +++ b/navigation/rail.go @@ -18,7 +18,7 @@ type NavRailOptions struct { } type NavRailResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -30,7 +30,7 @@ func NavRail(c *ui.Context, text string, items []NavItem, selected *int, o NavRa v := *selected res.Element = ui.RadioGroup(c, func() { for i, it := range items { - shell, _, st := pixel.Shell(c, func() *ui.Element { + shell, _, st := pixel.Shell(c, func() ui.Element { return ui.RadioBase(c, &v, i).Label(it.Label).Disabled(o.Disabled) }) shell.Size(k.D(22), k.D(22)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/navigation/sidebar.go b/navigation/sidebar.go index 4280d87..0e13174 100644 --- a/navigation/sidebar.go +++ b/navigation/sidebar.go @@ -27,7 +27,7 @@ type SidebarOptions struct { } type SidebarResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -46,7 +46,7 @@ func Sidebar(c *ui.Context, text string, sections []SidebarSection, selected *in for _, it := range sec.Items { i := n n++ - shell, _, st := pixel.Shell(c, func() *ui.Element { + shell, _, st := pixel.Shell(c, func() ui.Element { return ui.RadioBase(c, &v, i).Label(it.Label).Disabled(o.Disabled) }) shell.Size(k.D(w), k.D(pixel.LineHeight+2)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/navigation/steps.go b/navigation/steps.go index 32d4795..c4d2913 100644 --- a/navigation/steps.go +++ b/navigation/steps.go @@ -38,7 +38,7 @@ var waving = pixel.MustSprite(` type StepsOptions struct{} // Steps are flags on a path: done, waving, then locked. -func Steps(c *ui.Context, labels []string, current int, o StepsOptions) *ui.Element { +func Steps(c *ui.Context, labels []string, current int, o StepsOptions) ui.Element { k := pixel.K(c) if current < 0 || current >= len(labels) { panic(fmt.Sprintf("navigation: step %d of %d", current, len(labels))) diff --git a/navigation/tabs.go b/navigation/tabs.go index 12b2921..cc0840a 100644 --- a/navigation/tabs.go +++ b/navigation/tabs.go @@ -14,7 +14,7 @@ type TabsOptions struct { } type TabsResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -52,7 +52,7 @@ func tabs(c *ui.Context, text string, labels []string, selected *int, side bool, } p.List.Label(text).Children(func() { for i, l := range labels { - shell, _, st := pixel.Shell(c, func() *ui.Element { return p.Tab(i).Label(l).Disabled(o.Disabled) }) + shell, _, st := pixel.Shell(c, func() ui.Element { return p.Tab(i).Label(l).Disabled(o.Disabled) }) w, h := size(i), th if side { w = tw diff --git a/navigation/toc.go b/navigation/toc.go index 08df796..82a8fcd 100644 --- a/navigation/toc.go +++ b/navigation/toc.go @@ -29,7 +29,7 @@ type TableOfContentsOptions struct { } type TableOfContentsResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -42,7 +42,7 @@ func TableOfContents(c *ui.Context, text string, items []TocItem, current *int, v := *current res.Element = ui.RadioGroup(c, func() { for i, it := range items { - shell, _, st := pixel.Shell(c, func() *ui.Element { + shell, _, st := pixel.Shell(c, func() ui.Element { return ui.RadioBase(c, &v, i).Label(it.Title).Disabled(o.Disabled) }) shell.Size(k.D(w), k.D(pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/navigation/toolbar.go b/navigation/toolbar.go index 9d304a4..7ad4396 100644 --- a/navigation/toolbar.go +++ b/navigation/toolbar.go @@ -9,7 +9,7 @@ import ( type ToolbarOptions struct{} // Toolbar holds tool buttons as one Tab stop; arrows move. -func Toolbar(c *ui.Context, text string, o ToolbarOptions, content func()) *ui.Element { +func Toolbar(c *ui.Context, text string, o ToolbarOptions, content func()) ui.Element { k := pixel.K(c) return ui.Row(c).AlignSelf(ui.Start).Gap(k.D(2)).Padding(k.D(3)).AlignItems(ui.Center).Role(ui.RoleToolbar).Label(text).FocusGroup(ui.Horizontal).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/navigation/verticaltabs.go b/navigation/verticaltabs.go index 476a65f..9c66627 100644 --- a/navigation/verticaltabs.go +++ b/navigation/verticaltabs.go @@ -9,7 +9,7 @@ type VerticalTabsOptions struct { } type VerticalTabsResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/navigation/worldmap.go b/navigation/worldmap.go index c133029..e126630 100644 --- a/navigation/worldmap.go +++ b/navigation/worldmap.go @@ -46,7 +46,7 @@ type WorldMapOptions struct { } type WorldMapResult struct { - Element *ui.Element + Element ui.Element // Entered reports Enter, or a click, at the hero's node. Changed, Entered bool } @@ -66,7 +66,7 @@ func WorldMap(c *ui.Context, text string, nodes []MapNode, paths [][2]int, at *i panic(fmt.Sprintf("navigation: node %d of %d", *at, len(nodes))) } w, h := pixel.Or("WorldMap Width", o.Width, 240), pixel.Or("WorldMap Height", o.Height, 140) - 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.RoleGroup).Label(text).Description(nodes[*at].Name).Disabled(o.Disabled) }) wk := ui.Local(base, walkKey{}, func() walk { return walk{from: *at} }) diff --git a/overlay/alertdialog.go b/overlay/alertdialog.go index 4a9acfb..d518d4a 100644 --- a/overlay/alertdialog.go +++ b/overlay/alertdialog.go @@ -24,7 +24,7 @@ type AlertDialogOptions struct { } type AlertDialogResult struct { - Element *ui.Element + Element ui.Element // Pressed is the button pressed this frame, or -1. Pressed int Closed bool @@ -46,7 +46,7 @@ func AlertDialog(c *ui.Context, title, message string, open *bool, o AlertDialog was := *open res := AlertDialogResult{Pressed: -1} press := func(i int) { res.Pressed, *open = i, false } - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) panel.Width(k.D(220)).Role(ui.RoleAlertDialog).Label(title).Description(message).Focusable().FocusRing(false) if pixel.Shortcut(panel, 0, ui.KeyEnter) && !o.Disabled && !pixel.Current(c).Inert { diff --git a/overlay/choiceprompt.go b/overlay/choiceprompt.go index 4d68781..5c624ec 100644 --- a/overlay/choiceprompt.go +++ b/overlay/choiceprompt.go @@ -15,7 +15,7 @@ type ChoicePromptOptions struct { } type ChoicePromptResult struct { - Element *ui.Element + Element ui.Element // Chosen is the choice taken this frame, or -1. Chosen int // Canceled is Escape or a click outside. @@ -34,7 +34,7 @@ func ChoicePrompt(c *ui.Context, question string, choices []string, open *bool, cur := ui.Local(home, choiceKey{}, func() int { return 0 }) was := *open res := ChoicePromptResult{Chosen: -1} - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 4) panel.Width(k.D(180)).Label(question).Focusable().FocusRing(false).Children(func() { pixel.Harbor(c, panel, func() { diff --git a/overlay/coachmark.go b/overlay/coachmark.go index 9a19929..0c2d11c 100644 --- a/overlay/coachmark.go +++ b/overlay/coachmark.go @@ -16,7 +16,7 @@ type CoachmarkOptions struct { } type CoachmarkResult struct { - Element *ui.Element + Element ui.Element Next bool } @@ -24,7 +24,7 @@ type CoachmarkResult struct { const coachWidth = 140 // Coachmark points a bubble at target: step count, NEXT ▶. -func Coachmark(c *ui.Context, target *ui.Element, message string, step, steps int, o CoachmarkOptions) CoachmarkResult { +func Coachmark(c *ui.Context, target ui.Element, message string, step, steps int, o CoachmarkOptions) CoachmarkResult { k := pixel.K(c) if step < 1 || step > steps { panic(fmt.Sprintf("overlay: Coachmark step %d of %d", step, steps)) diff --git a/overlay/confirmdialog.go b/overlay/confirmdialog.go index 09f2462..ef7c704 100644 --- a/overlay/confirmdialog.go +++ b/overlay/confirmdialog.go @@ -15,7 +15,7 @@ type ConfirmDialogOptions struct { } type ConfirmDialogResult struct { - Element *ui.Element + Element ui.Element // Canceled is NO, Escape or a click outside. Confirmed, Canceled bool } @@ -29,7 +29,7 @@ func ConfirmDialog(c *ui.Context, message string, open *bool, o ConfirmDialogOpt cur := ui.Local(home, confirmKey{}, func() int { return 1 }) was := *open var res ConfirmDialogResult - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) panel.Width(k.D(180)).Label(k.Msg("sure")).Description(message).Focusable().FocusRing(false).Children(func() { pixel.Harbor(c, panel, func() { diff --git a/overlay/dialog.go b/overlay/dialog.go index cf0fecd..7c5cf0c 100644 --- a/overlay/dialog.go +++ b/overlay/dialog.go @@ -20,7 +20,7 @@ type DialogOptions struct { type DialogResult struct { // Element is the window, nil while closed. - Element *ui.Element + Element ui.Element // Action is the button clicked this frame, or -1. Action int // Closed is Escape or a click outside. @@ -33,7 +33,7 @@ func Dialog(c *ui.Context, title string, open *bool, o DialogOptions, content fu w := pixel.Or("Dialog Width", o.Width, 220) was := *open res := DialogResult{Action: -1} - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) // The panel holds the focus when nothing inside can. panel.Width(k.D(w)).Label(title).Focusable().FocusRing(false).Children(func() { diff --git a/overlay/dialoguebox.go b/overlay/dialoguebox.go index 9e0e42d..40517f6 100644 --- a/overlay/dialoguebox.go +++ b/overlay/dialoguebox.go @@ -29,7 +29,7 @@ type DialogueBoxOptions struct { } type DialogueBoxResult struct { - Element *ui.Element + Element ui.Element // Changed turns a page; Done passes the last one. Changed, Done bool } diff --git a/overlay/drawer.go b/overlay/drawer.go index 6cb2c33..a3d6cee 100644 --- a/overlay/drawer.go +++ b/overlay/drawer.go @@ -19,7 +19,7 @@ type DrawerOptions struct { } type DrawerResult struct { - Element *ui.Element + Element ui.Element // Closed is Escape or a click outside. Closed bool } @@ -35,7 +35,7 @@ func Drawer(c *ui.Context, text string, open *bool, o DrawerOptions, content fun off := k.D(arriving(c, home, *open) * size / 4) was := *open var res DrawerResult - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) panel.Absolute().Label(text).Padding(k.D(8)).Focusable().FocusRing(false) switch o.Side { diff --git a/overlay/handback_test.go b/overlay/handback_test.go new file mode 100644 index 0000000..41c1201 --- /dev/null +++ b/overlay/handback_test.go @@ -0,0 +1,42 @@ +package overlay_test + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input" + "git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/overlay" +) + +func TestDialogsHandTheFocusBack(t *testing.T) { + kinds := map[string]func(c *ui.Context, open *bool){ + "confirm": func(c *ui.Context, open *bool) { + overlay.ConfirmDialog(c, "Sure?", open, overlay.ConfirmDialogOptions{}) + }, + "choice": func(c *ui.Context, open *bool) { + overlay.ChoicePrompt(c, "Pick", []string{"A", "B"}, open, overlay.ChoicePromptOptions{}) + }, + "prompt": func(c *ui.Context, open *bool) { + v := "" + overlay.PromptDialog(c, "Name", &v, open, overlay.PromptDialogOptions{}) + }, + "pause": func(c *ui.Context, open *bool) { overlay.PauseMenu(c, open, overlay.PauseMenuOptions{}) }, + } + for name, show := range kinds { + open := false + tt := view(500, 400, newClock(), false, func(c *ui.Context) { + if input.Button(c, "Opener", input.ButtonOptions{}).Clicked { + open = true + } + show(c, &open) + }) + if err := tt.Click("Opener"); err != nil || !open || tt.Focused("Opener") { + t.Fatalf("%s: did not open with the focus inside (%v)", name, err) + } + tt.Key(0, ui.KeyEscape) + if open || !tt.Focused("Opener") { + t.Fatalf("%s: Escape left it open or the focus lost", name) + } + } +} diff --git a/overlay/hovercard.go b/overlay/hovercard.go index 14c9306..6d1ad2a 100644 --- a/overlay/hovercard.go +++ b/overlay/hovercard.go @@ -14,7 +14,7 @@ type HoverCardOptions struct { type hoverKey struct{} // HoverCard shows card below trigger while either is hovered. -func HoverCard(c *ui.Context, text string, o HoverCardOptions, trigger func(), card func()) *ui.Element { +func HoverCard(c *ui.Context, text string, o HoverCardOptions, trigger func(), card func()) ui.Element { k := pixel.K(c) w := pixel.Or("HoverCard Width", o.Width, 160) wrap := ui.Box(c) diff --git a/overlay/keyhints.go b/overlay/keyhints.go index 3c05cb7..82b065d 100644 --- a/overlay/keyhints.go +++ b/overlay/keyhints.go @@ -15,7 +15,7 @@ type KeyHint struct { type KeyHintsOptions struct{} // KeyHints shows controls, as Ⓐ CONFIRM Ⓑ BACK. -func KeyHints(c *ui.Context, hints []KeyHint, o KeyHintsOptions) *ui.Element { +func KeyHints(c *ui.Context, hints []KeyHint, o KeyHintsOptions) ui.Element { k := pixel.K(c) return ui.Row(c).Gap(k.D(10)).Padding(k.D(3), k.D(6)).AlignItems(ui.Center).Role(ui.RoleGroup).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/overlay/lightbox.go b/overlay/lightbox.go index b64845e..92bb08f 100644 --- a/overlay/lightbox.go +++ b/overlay/lightbox.go @@ -16,7 +16,7 @@ type LightboxOptions struct { } type LightboxResult struct { - Element *ui.Element + Element ui.Element // Changed turns the picture; Closed is ×, Escape or outside. Changed, Closed bool } @@ -31,7 +31,7 @@ func Lightbox(c *ui.Context, text string, n int, at *int, open *bool, o Lightbox off := o.Disabled || pixel.Current(c).Inert turn := func(d int) { *at = (*at + d + n) % n } var res LightboxResult - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { deep, light := dark(k) dim(k, back, deep, 13) left, right := pixel.Shortcut(panel, 0, ui.KeyLeft), pixel.Shortcut(panel, 0, ui.KeyRight) diff --git a/overlay/notification.go b/overlay/notification.go index 5ec0aa9..cdaf7c7 100644 --- a/overlay/notification.go +++ b/overlay/notification.go @@ -31,7 +31,7 @@ type NotificationsOptions struct { type NotificationsResult struct { // Element holds the cards on screen. - Element *ui.Element + Element ui.Element // Dismissed is the card × closed this frame, or nil. Dismissed *Notification } diff --git a/overlay/overlay.go b/overlay/overlay.go index 6c20ef7..a87626a 100644 --- a/overlay/overlay.go +++ b/overlay/overlay.go @@ -10,7 +10,7 @@ import ( ) // dim checkers the backdrop of a dialog. -func dim(k pixel.Kit, back *ui.Element, s pixel.Slot, level int) { +func dim(k pixel.Kit, back ui.Element, s pixel.Slot, level int) { back.Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) g.Veil(0, 0, g.W, g.H, s, level) @@ -33,7 +33,7 @@ func choose(c *ui.Context, text string, items []string, cur *int, digits, off bo off = off || pixel.Current(c).Inert box := ui.Column(c).FocusRing(false).Role(ui.RoleList).Label(text) if !off { - box.Focusable().AutoFocus() + pixel.AutoFocus(c, box.Focusable()) } pixel.Park(c, box, off) picked := -1 @@ -92,23 +92,24 @@ type hintKey struct{} // hinting is when hover began, and a press-given focus. type hinting struct { - since time.Time - pressed bool + since time.Time + focus, pressed bool } // hint holds after resting wait, or on keyboard focus. -func hint(c *ui.Context, e *ui.Element, wait time.Duration) bool { +func hint(c *ui.Context, e ui.Element, wait time.Duration) bool { k := pixel.K(c) s := ui.Local(e, hintKey{}, func() hinting { return hinting{} }) _, _, over := e.PointerPosition() hovered, focus := e.Hovered(), e.FocusWithin() switch { - case over && !hovered: - // A press inside: its focus is not the keyboard's. + case focus && !s.focus && over: + // Focus arriving under the pointer came from a press. s.pressed = true case !focus: s.pressed = false } + s.focus = focus rested := false if hovered { if s.since.IsZero() { @@ -123,7 +124,7 @@ func hint(c *ui.Context, e *ui.Element, wait time.Duration) bool { } // arriving counts the steps, of four, a panel still slides. -func arriving(c *ui.Context, home *ui.Element, open bool) int { +func arriving(c *ui.Context, home ui.Element, open bool) int { k := pixel.K(c) on := 0 if open { diff --git a/overlay/pausemenu.go b/overlay/pausemenu.go index 275f50f..3557ca1 100644 --- a/overlay/pausemenu.go +++ b/overlay/pausemenu.go @@ -13,7 +13,7 @@ type PauseMenuOptions struct { } type PauseMenuResult struct { - Element *ui.Element + Element ui.Element // Chosen is RESUME 0, OPTIONS 1, QUIT 2, or -1. Chosen int } @@ -28,7 +28,7 @@ func PauseMenu(c *ui.Context, paused *bool, o PauseMenuOptions) PauseMenuResult was := *paused res := PauseMenuResult{Chosen: -1} items := []string{k.Msg("resume"), k.Msg("options"), k.Msg("quit")} - res.Element = ui.DialogBase(c, paused, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, paused, func(back, panel ui.Element) { deep, light := dark(k) dim(k, back, deep, 10) title := k.Msg("paused") diff --git a/overlay/popconfirm.go b/overlay/popconfirm.go index f004ade..80ac69c 100644 --- a/overlay/popconfirm.go +++ b/overlay/popconfirm.go @@ -14,7 +14,7 @@ type PopconfirmOptions struct { } type PopconfirmResult struct { - Element *ui.Element + Element ui.Element // Canceled is NO, Escape or a click outside. Confirmed, Canceled bool } diff --git a/overlay/popover.go b/overlay/popover.go index a6fb87e..52b40b9 100644 --- a/overlay/popover.go +++ b/overlay/popover.go @@ -15,11 +15,11 @@ type PopoverOptions struct { const notch = 4 // Popover opens a panel below anchor, notched toward it. -func Popover(c *ui.Context, text string, anchor *ui.Element, open *bool, o PopoverOptions, content func()) *ui.Element { +func Popover(c *ui.Context, text string, anchor ui.Element, open *bool, o PopoverOptions, content func()) ui.Element { k := pixel.K(c) w := pixel.Or("Popover Width", o.Width, 0) ab := anchor.Bounds() - return ui.PopoverBase(c, anchor, open, func(panel *ui.Element) { + return ui.PopoverBase(c, anchor, open, func(panel ui.Element) { if w > 0 { panel.Width(k.D(w)) } diff --git a/overlay/progressdialog.go b/overlay/progressdialog.go index e1cdd60..f688935 100644 --- a/overlay/progressdialog.go +++ b/overlay/progressdialog.go @@ -15,7 +15,7 @@ type ProgressDialogOptions struct { } type ProgressDialogResult struct { - Element *ui.Element + Element ui.Element // Canceled is CANCEL, Escape or a click outside. Canceled bool } @@ -25,7 +25,7 @@ func ProgressDialog(c *ui.Context, title string, value float64, open *bool, o Pr k := pixel.K(c) was := *open var res ProgressDialogResult - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) panel.Width(k.D(200)).Label(title).Focusable().FocusRing(false).Children(func() { pixel.Harbor(c, panel, func() { diff --git a/overlay/promptdialog.go b/overlay/promptdialog.go index d067b71..0de3275 100644 --- a/overlay/promptdialog.go +++ b/overlay/promptdialog.go @@ -17,7 +17,7 @@ type PromptDialogOptions struct { } type PromptDialogResult struct { - Element *ui.Element + Element ui.Element // Canceled is CANCEL, Escape or a click outside. Submitted, Canceled bool } @@ -27,14 +27,14 @@ func PromptDialog(c *ui.Context, title string, value *string, open *bool, o Prom k := pixel.K(c) was := *open var res PromptDialogResult - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) panel.Width(k.D(220)).Label(title).Focusable().FocusRing(false).Children(func() { pixel.Harbor(c, panel, func() { layout.Window(c, layout.WindowOptions{Title: title}, func() { ui.Column(c).Gap(k.D(6)).AlignItems(ui.Stretch).Children(func() { f := input.TextInput(c, title, value, input.TextInputOptions{Placeholder: o.Placeholder, MaxLength: o.MaxLength, Width: 196, Disabled: o.Disabled}) - f.Input.AutoFocus() + pixel.AutoFocus(c, f.Input) // A Shortcut, not Submitted: the frame builds again closed. if pixel.Shortcut(f.Element, 0, ui.KeyEnter) && !o.Disabled { res.Submitted, *open = true, false diff --git a/overlay/sheet.go b/overlay/sheet.go index bf3fe31..de746b3 100644 --- a/overlay/sheet.go +++ b/overlay/sheet.go @@ -15,7 +15,7 @@ type SheetOptions struct { } type SheetResult struct { - Element *ui.Element + Element ui.Element // Closed is a drag down, Escape or a click outside. Closed bool } @@ -32,7 +32,7 @@ func Sheet(c *ui.Context, text string, open *bool, o SheetOptions, content func( rise := k.D(arriving(c, home, *open) * h / 4) was := *open var res SheetResult - res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) { + res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) { dim(k, back, pixel.Back, 8) panel.Label(text).Focusable().FocusRing(false).Draw(func(p *ui.Painter, r ui.Rect) { g := k.Canvas(p, r) diff --git a/overlay/spotlight.go b/overlay/spotlight.go index 9016db8..8513e24 100644 --- a/overlay/spotlight.go +++ b/overlay/spotlight.go @@ -9,11 +9,11 @@ import ( type SpotlightOptions struct{} // Spotlight dithers all but target and captions it. -func Spotlight(c *ui.Context, target *ui.Element, caption string, o SpotlightOptions) *ui.Element { +func Spotlight(c *ui.Context, target ui.Element, caption string, o SpotlightOptions) ui.Element { k := pixel.K(c) b := target.Bounds() deep, _ := dark(k) - var veil *ui.Element + var veil ui.Element ui.Overlay(c, func() { veil = ui.Box(c).Absolute().Left(0).Top(0).Right(0).Bottom(0).PassThrough().Role(ui.RoleStatus).Label(caption) veil.Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/overlay/titlecard.go b/overlay/titlecard.go index fac35a4..15b03fe 100644 --- a/overlay/titlecard.go +++ b/overlay/titlecard.go @@ -16,7 +16,7 @@ type TitleCardOptions struct { } // TitleCard dithers in, holds title, dithers out; fills its parent. -func TitleCard(c *ui.Context, title string, o TitleCardOptions) *ui.Element { +func TitleCard(c *ui.Context, title string, o TitleCardOptions) ui.Element { k := pixel.K(c) if o.Hold < 0 { panic("overlay: TitleCard Hold is negative") diff --git a/overlay/tooltip.go b/overlay/tooltip.go index a97cf96..0a0a737 100644 --- a/overlay/tooltip.go +++ b/overlay/tooltip.go @@ -15,7 +15,7 @@ type TooltipOptions struct{} const tipWidth = 160 // Tooltip bubbles text after 500 ms, or on keyboard focus. -func Tooltip(c *ui.Context, text string, o TooltipOptions, trigger func()) *ui.Element { +func Tooltip(c *ui.Context, text string, o TooltipOptions, trigger func()) ui.Element { k := pixel.K(c) wrap := ui.Box(c) show := hint(c, wrap, 500*time.Millisecond) @@ -32,12 +32,12 @@ func Tooltip(c *ui.Context, text string, o TooltipOptions, trigger func()) *ui.E } // bubble floats content by b; w keeps it inside. -func bubble(c *ui.Context, b ui.Rect, content func(), w int) *ui.Element { +func bubble(c *ui.Context, b ui.Rect, content func(), w int) ui.Element { k := pixel.K(c) ww, wh := c.Size() x := max(0, min(b.X+b.W/2-k.D(8), ww-k.D(w))) at := max(3, int((b.X+b.W/2-x)/k.Dot())) - var e *ui.Element + var e ui.Element ui.Overlay(c, func() { e = ui.Box(c).Absolute().Left(x).PassThrough() tail := display.Bottom diff --git a/pixel/clock.go b/pixel/clock.go index 7845a18..b9d0a17 100644 --- a/pixel/clock.go +++ b/pixel/clock.go @@ -97,7 +97,7 @@ type heading struct { } // Toward steps 0–n toward on, turning from where it is. -func (k Kit) Toward(c *ui.Context, e *ui.Element, on bool, fps, n int) int { +func (k Kit) Toward(c *ui.Context, e ui.Element, on bool, fps, n int) int { checkFPS(fps) if n < 1 { panic(fmt.Sprintf("pixel: Toward needs steps, got %d", n)) @@ -137,7 +137,7 @@ type shot struct { } // Shot steps a cued one-shot; Reduce Motion spends it whole. -func (k Kit) Shot(c *ui.Context, e *ui.Element, cue, fps, done int) int { +func (k Kit) Shot(c *ui.Context, e ui.Element, cue, fps, done int) int { checkFPS(fps) // Before any change the zero start reads as done. s := ui.Local(e, shotKey{}, func() shot { return shot{cue: cue} }) @@ -162,7 +162,7 @@ type cued struct { } // Cue is when n last changed on e, zero before. -func (k Kit) Cue(e *ui.Element, n int) time.Time { +func (k Kit) Cue(e ui.Element, n int) time.Time { s := ui.Local(e, cueKey{}, func() cued { return cued{n: n} }) if s.n != n { s.n, s.at = n, k.now diff --git a/pixel/editor.go b/pixel/editor.go index 72008cc..1959f2d 100644 --- a/pixel/editor.go +++ b/pixel/editor.go @@ -23,7 +23,7 @@ type EditorOptions struct { type Edit struct { // Element is laid out; Input takes the focus. - Element, Input *ui.Element + Element, Input ui.Element Changed, Submitted, Focused bool // Line is the caret's line, from 0. Line int @@ -53,7 +53,7 @@ func Editor(c *ui.Context, value *string, o EditorOptions) Edit { if o.EnterSends && !o.Multiline { panic("pixel: EnterSends needs a multi-line field") } - shell, base, _ := Shell(c, func() *ui.Element { + shell, base, _ := Shell(c, func() ui.Element { return ui.Box(c).Focusable().Role(ui.RoleTextField).Label(o.Label).Disabled(o.Disabled) }) s := ui.Local(base, editKey{}, func() editState { return editState{} }) @@ -73,7 +73,7 @@ func Editor(c *ui.Context, value *string, o EditorOptions) Edit { base.Description(*value) } width, check := WrapWidth(c, shell) - env := editEnv{multi: o.Multiline, sends: o.EnterSends, maxLen: o.MaxLength, mask: o.Mask, width: width, dot: k.dot, read: c.ReadClipboard, write: func(v string) { s.clip, s.clipped = v, true }} + env := editEnv{multi: o.Multiline, sends: o.EnterSends, maxLen: o.MaxLength, mask: o.Mask, width: width, dot: k.dot, read: c.Services().ReadClipboard, write: func(v string) { s.clip, s.clipped = v, true }} shown := s.view(o.Mask, true) caret := s.caret + s.composeCaret lines := editLayout(shown, width, o.Multiline) diff --git a/pixel/element.go b/pixel/element.go index b969301..8277822 100644 --- a/pixel/element.go +++ b/pixel/element.go @@ -17,7 +17,7 @@ type TextOptions struct { } // Text is a pixel-font label that screen readers read. -func Text(c *ui.Context, s string, o TextOptions) *ui.Element { +func Text(c *ui.Context, s string, o TextOptions) ui.Element { k := K(c) scale := scaleOr1(o.Scale) e := ui.Box(c).Label(s).Role(ui.RoleText) @@ -65,7 +65,7 @@ func Wrap(s string, width, maxLines int) []string { type wrapKey struct{} // WrapWidth is e's last painted width; Draw must call check. -func WrapWidth(c *ui.Context, e *ui.Element) (int, func(r ui.Rect)) { +func WrapWidth(c *ui.Context, e ui.Element) (int, func(r ui.Rect)) { dot := K(c).dot w := ui.Local(e, wrapKey{}, func() int { return 0 }) used := *w @@ -89,7 +89,7 @@ func scaleOr1(s int) int { type State struct{ Hover, Press, Focus, Disabled bool } // Shell wraps base in an enabled box that paints. -func Shell(c *ui.Context, base func() *ui.Element) (shell, b *ui.Element, st State) { +func Shell(c *ui.Context, base func() ui.Element) (shell, b ui.Element, st State) { shell = ui.Box(c) shell.Children(func() { b = base().Fill().FocusRing(false) }) own, inert := b.IsDisabled(), current(c).cur.Inert @@ -103,7 +103,7 @@ func Shell(c *ui.Context, base func() *ui.Element) (shell, b *ui.Element, st Sta } // Shortcut is e's key this frame, while e is enabled. -func Shortcut(e *ui.Element, mods ui.Modifiers, key ui.Key) bool { +func Shortcut(e ui.Element, mods ui.Modifiers, key ui.Key) bool { // MyGo still hands keys to a base just turned off. return e.Shortcut(mods, key) && !e.IsDisabled() } @@ -111,10 +111,10 @@ func Shortcut(e *ui.Element, mods ui.Modifiers, key ui.Key) bool { type parkKey struct{} // Park moves focus off e while off, back once on. -func Park(c *ui.Context, e *ui.Element, off bool) { +func Park(c *ui.Context, e ui.Element, off bool) { parked := ui.Local(e, parkKey{}, func() bool { return false }) spot := c.Root() - if h := current(c).harbor; h != nil { + if h := current(c).harbor; h.Valid() { spot = h } switch { @@ -130,8 +130,20 @@ func Park(c *ui.Context, e *ui.Element, off bool) { } } +type autoKey struct{} + +// AutoFocus focuses e as it appears, after overlays note openers. +func AutoFocus(c *ui.Context, e ui.Element) { + first := false + h := ui.Local(e, autoKey{}, func() ui.Handle { first = true; return ui.Handle{} }) + e.Bind(h) + if first { + h.Focus(c) + } +} + // Harbor keeps focus in e while controls in fn rest. -func Harbor(c *ui.Context, e *ui.Element, fn func()) { +func Harbor(c *ui.Context, e ui.Element, fn func()) { f := current(c) prev := f.harbor f.harbor = e diff --git a/pixel/element_test.go b/pixel/element_test.go index da0e104..db32321 100644 --- a/pixel/element_test.go +++ b/pixel/element_test.go @@ -85,8 +85,8 @@ func TestClock(t *testing.T) { if step != 0 || busy == 0 { t.Fatalf("under Reduce Motion: Step %d (want 0), Busy %d", step, busy) } - if due := time.Until(pixel.Due()); due <= 0 || due > 125*time.Millisecond { - t.Fatalf("Due in %v, want the 8 fps wait on the real clock", due) + if due := pixel.Due().Sub(tt.Now()); due <= 0 || due > 125*time.Millisecond { + t.Fatalf("Due in %v, want the 8 fps wait on the frame clock", due) } pixel.ResetDue() if !pixel.Due().IsZero() { @@ -166,7 +166,7 @@ func shellView(disabled *bool, clicks *int, st *pixel.State) func(c *ui.Context) ui.Box(c).Size(k.D(16), k.D(4)) ui.Row(c).Children(func() { ui.Box(c).Size(k.D(16), k.D(8)) - shell, base, s := pixel.Shell(c, func() *ui.Element { + shell, base, s := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label("go").Disabled(*disabled) }) *st = s diff --git a/pixel/pad.go b/pixel/pad.go index 328e84a..c06db7f 100644 --- a/pixel/pad.go +++ b/pixel/pad.go @@ -3,9 +3,9 @@ package pixel import "github.com/egoist/mygo/ui" // Pad is a focusable grid; it returns the picked cell. -func Pad(c *ui.Context, text string, cols, n, cw, ch int, cur *int, disabled bool) (shell, base *ui.Element, st State, picked int) { +func Pad(c *ui.Context, text string, cols, n, cw, ch int, cur *int, disabled bool) (shell, base ui.Element, st State, picked int) { k := K(c) - shell, base, st = Shell(c, func() *ui.Element { + shell, base, st = Shell(c, func() ui.Element { return ui.Box(c).Focusable().Role(ui.RoleGroup).Label(text).Disabled(disabled) }) shell.Size(k.D(cols*cw), k.D((n+cols-1)/cols*ch)) diff --git a/pixel/settings.go b/pixel/settings.go index 27c41f0..bb9cee1 100644 --- a/pixel/settings.go +++ b/pixel/settings.go @@ -47,11 +47,12 @@ func (k Kit) Now() time.Time { return k.now } func (k Kit) Reduce() bool { return k.reduce } type frame struct { - cur Settings - at time.Time + cur Settings + // pass is the root of the pass Use ran in. + pass ui.Element typing bool // harbor holds the focus of controls turning off. - harbor *ui.Element + harbor ui.Element } type frameKey struct{} @@ -63,7 +64,7 @@ func Use(c *ui.Context, s Settings) { if f.cur.Palette != s.Palette { slog.Debug("8bit: palette", "name", s.Palette.Name) } - f.cur, f.at, f.typing, f.harbor = s, c.Now(), false, nil + f.cur, f.pass, f.typing, f.harbor = s, c.Root(), false, ui.Element{} c.SetTheme(theme(s.Palette)) } @@ -103,7 +104,7 @@ func frameOf(c *ui.Context) *frame { func current(c *ui.Context) *frame { f := frameOf(c) - if !f.at.Equal(c.Now()) { + if f.pass != c.Root() { panic("pixel: Use was not called in this frame") } return f diff --git a/project/assigneepicker.go b/project/assigneepicker.go index bf9de14..1239040 100644 --- a/project/assigneepicker.go +++ b/project/assigneepicker.go @@ -16,7 +16,7 @@ type AssigneePickerOptions struct { } type AssigneePickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -38,7 +38,7 @@ func AssigneePicker(c *ui.Context, text string, party []string, chosen *int, o A } // The shell draws the seat; the radio stays bare. sel := *chosen - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.RadioBase(c, &sel, i).Label(name).Disabled(o.Disabled) }) // The radio picks before it is told it is off. diff --git a/project/burndown.go b/project/burndown.go index 377efcc..41a43cd 100644 --- a/project/burndown.go +++ b/project/burndown.go @@ -17,7 +17,7 @@ type BurndownOptions struct { } // Burndown draws the ideal line and the work left. -func Burndown(c *ui.Context, text string, total float64, days int, left []float64, o BurndownOptions) *ui.Element { +func Burndown(c *ui.Context, text string, total float64, days int, left []float64, o BurndownOptions) ui.Element { k := pixel.K(c) if !(total > 0) || math.IsInf(total, 0) || days < 1 || len(left) > days+1 { panic(fmt.Sprintf("project: Burndown of %v over %d days, %d points", total, days, len(left))) diff --git a/project/checklist.go b/project/checklist.go index 1a4622b..b7f325b 100644 --- a/project/checklist.go +++ b/project/checklist.go @@ -18,7 +18,7 @@ type ChecklistOptions struct { } type ChecklistResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/project/issuecard.go b/project/issuecard.go index 8088bad..338c545 100644 --- a/project/issuecard.go +++ b/project/issuecard.go @@ -26,7 +26,7 @@ type IssueCardOptions struct { } type IssueCardResult struct { - Element *ui.Element + Element ui.Element Clicked bool } @@ -51,7 +51,7 @@ func IssueCard(c *ui.Context, is Issue, o IssueCardOptions) IssueCardResult { w := pixel.Or("IssueCard Width", o.Width, 160) lines := pixel.Wrap(is.Title, w-8, 2) // The shell draws the card; the button stays bare. - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(is.ID + " " + is.Title).Disabled(o.Disabled) }) base.Description(strings.Join(said, ", ")) diff --git a/project/labelpicker.go b/project/labelpicker.go index a57c084..95ee25d 100644 --- a/project/labelpicker.go +++ b/project/labelpicker.go @@ -17,7 +17,7 @@ type LabelPickerOptions struct { } type LabelPickerResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/project/milestones.go b/project/milestones.go index b3deb99..52d97e7 100644 --- a/project/milestones.go +++ b/project/milestones.go @@ -64,7 +64,7 @@ var ( ) // Milestones plants flags on a path; a hero marks today. -func Milestones(c *ui.Context, text string, ms []Milestone, o MilestonesOptions) *ui.Element { +func Milestones(c *ui.Context, text string, ms []Milestone, o MilestonesOptions) ui.Element { k := pixel.K(c) if len(ms) == 0 { panic("project: Milestones has none") diff --git a/project/pomodoro.go b/project/pomodoro.go index 512395c..5e1d304 100644 --- a/project/pomodoro.go +++ b/project/pomodoro.go @@ -18,7 +18,7 @@ type PomodoroOptions struct { } type PomodoroResult struct { - Element *ui.Element + Element ui.Element // Changed is START, PAUSE or RESET; Rang ends a phase. Changed, Rang bool } diff --git a/project/priority.go b/project/priority.go index 6501b59..afb7b0e 100644 --- a/project/priority.go +++ b/project/priority.go @@ -56,7 +56,7 @@ var bars = [3]*pixel.Sprite{pixel.MustSprite(` var levelInks = [...]pixel.Slot{pixel.Good, pixel.Warn, pixel.Bad, pixel.Bad} // Priority is a level's icon; critical is on fire. -func Priority(c *ui.Context, l Level, o PriorityOptions) *ui.Element { +func Priority(c *ui.Context, l Level, o PriorityOptions) ui.Element { k := pixel.K(c) say := l.word(k) s := pixel.Or("Priority Scale", o.Scale, 1) diff --git a/project/project.go b/project/project.go index 0fe11e5..ed08ea2 100644 --- a/project/project.go +++ b/project/project.go @@ -53,7 +53,7 @@ func hms(d time.Duration) string { } // show draws s unnamed, beside an element that names it. -func show(c *ui.Context, k pixel.Kit, s string, slot pixel.Slot, w int) *ui.Element { +func show(c *ui.Context, k pixel.Kit, s string, slot pixel.Slot, w int) ui.Element { return ui.Box(c).Size(k.D(w), k.D(pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) { k.Canvas(p, r).Text(0, 0, pixel.Wrap(s, w, 1)[0], slot, 1) }) diff --git a/project/questboard.go b/project/questboard.go index ccc2a0b..c520988 100644 --- a/project/questboard.go +++ b/project/questboard.go @@ -19,7 +19,7 @@ type QuestBoardOptions struct { } type QuestBoardResult struct { - Element *ui.Element + Element ui.Element // Moved reports a card that changed column or place. Moved bool } @@ -47,7 +47,7 @@ func QuestBoard(c *ui.Context, text string, cols *[3][]string, o QuestBoardOptio } most = max(most, len(col)) } - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) b := ui.Local(base, boardKey{}, func() board { return board{} }) diff --git a/project/roadmap.go b/project/roadmap.go index 2a8b060..f01fee4 100644 --- a/project/roadmap.go +++ b/project/roadmap.go @@ -36,7 +36,7 @@ var capSlots = []pixel.Slot{pixel.Info, pixel.Good, pixel.Warn, pixel.Magic, pix const laneH = pixel.LineHeight + 8 // Roadmap lays bars on lanes under months, from from's. -func Roadmap(c *ui.Context, text string, from time.Time, lanes []Lane, o RoadmapOptions) *ui.Element { +func Roadmap(c *ui.Context, text string, from time.Time, lanes []Lane, o RoadmapOptions) ui.Element { k := pixel.K(c) months, mw := pixel.Or("Roadmap Months", o.Months, 6), pixel.Or("Roadmap MonthWidth", o.MonthWidth, 44) if len(lanes) == 0 || months > 120 { diff --git a/project/sprintpanel.go b/project/sprintpanel.go index 8b43c2e..ccc1615 100644 --- a/project/sprintpanel.go +++ b/project/sprintpanel.go @@ -26,7 +26,7 @@ type SprintPanelOptions struct { const day = 24 * time.Hour // SprintPanel shows a sprint's points done and days left. -func SprintPanel(c *ui.Context, s Sprint, o SprintPanelOptions) *ui.Element { +func SprintPanel(c *ui.Context, s Sprint, o SprintPanelOptions) ui.Element { k := pixel.K(c) if s.Name == "" || !s.Start.Before(s.End) || s.Total < 1 || s.Done < 0 || s.Done > s.Total { panic(fmt.Sprintf("project: SprintPanel %q from %v to %v, %d of %d", s.Name, s.Start, s.End, s.Done, s.Total)) diff --git a/project/taskitem.go b/project/taskitem.go index b5ea9e3..b54f4c9 100644 --- a/project/taskitem.go +++ b/project/taskitem.go @@ -24,7 +24,7 @@ type TaskItemOptions struct { } type TaskItemResult struct { - Element *ui.Element + Element ui.Element Changed bool } diff --git a/project/tasklist.go b/project/tasklist.go index 6fcbba9..5574b02 100644 --- a/project/tasklist.go +++ b/project/tasklist.go @@ -17,7 +17,7 @@ type TaskListOptions struct { } type TaskListResult struct { - Element *ui.Element + Element ui.Element // Changed reports a task checked or a filter turned. Changed bool } diff --git a/project/timetracker.go b/project/timetracker.go index d1d895f..f956e6e 100644 --- a/project/timetracker.go +++ b/project/timetracker.go @@ -25,7 +25,7 @@ type TimeTrackerOptions struct { } type TimeTrackerResult struct { - Element *ui.Element + Element ui.Element Started, Stopped bool } diff --git a/project/workloadbar.go b/project/workloadbar.go index d3077b9..b4a3070 100644 --- a/project/workloadbar.go +++ b/project/workloadbar.go @@ -26,7 +26,7 @@ type WorkloadBarOptions struct { } // WorkloadBar shows each member's load against capacity. -func WorkloadBar(c *ui.Context, text string, loads []Load, o WorkloadBarOptions) *ui.Element { +func WorkloadBar(c *ui.Context, text string, loads []Load, o WorkloadBarOptions) ui.Element { k := pixel.K(c) cp := o.Capacity if cp == 0 { diff --git a/shop/bidticker.go b/shop/bidticker.go index b688650..b47d1b1 100644 --- a/shop/bidticker.go +++ b/shop/bidticker.go @@ -25,7 +25,7 @@ type BidTickerOptions struct { } // BidTicker lists bids, newest first, crowning the highest. -func BidTicker(c *ui.Context, text string, bids []Bid, o BidTickerOptions) *ui.Element { +func BidTicker(c *ui.Context, text string, bids []Bid, o BidTickerOptions) ui.Element { k := pixel.K(c) top := -1 for i, b := range bids { diff --git a/shop/cart.go b/shop/cart.go index c9e3af0..28a029e 100644 --- a/shop/cart.go +++ b/shop/cart.go @@ -28,7 +28,7 @@ type CartOptions struct { } type CartResult struct { - Element *ui.Element + Element ui.Element Changed bool // Removed is the line whose ✕ was pressed, else -1. Removed int diff --git a/shop/checkout.go b/shop/checkout.go index 7c2c3fa..d1622bf 100644 --- a/shop/checkout.go +++ b/shop/checkout.go @@ -17,7 +17,7 @@ type CheckoutOptions struct { } type CheckoutResult struct { - Element *ui.Element + Element ui.Element // Done reports the order placed this frame. Changed, Done bool } diff --git a/shop/coupon.go b/shop/coupon.go index 9907fd1..25b39df 100644 --- a/shop/coupon.go +++ b/shop/coupon.go @@ -16,7 +16,7 @@ type CouponOptions struct { } type CouponResult struct { - Element *ui.Element + Element ui.Element Copied bool } diff --git a/shop/currencyconvert.go b/shop/currencyconvert.go index e58dca8..26fbf63 100644 --- a/shop/currencyconvert.go +++ b/shop/currencyconvert.go @@ -18,7 +18,7 @@ type CurrencyConvertOptions struct { } type CurrencyConvertResult struct { - Element *ui.Element + Element ui.Element // Converted is the gems bought this frame. Converted int } diff --git a/shop/dailyreward.go b/shop/dailyreward.go index 9808ab9..3ac0d57 100644 --- a/shop/dailyreward.go +++ b/shop/dailyreward.go @@ -17,7 +17,7 @@ type DailyRewardOptions struct { } type DailyRewardResult struct { - Element *ui.Element + Element ui.Element Claimed bool } diff --git a/shop/pricetag.go b/shop/pricetag.go index 03f313a..3e31fd3 100644 --- a/shop/pricetag.go +++ b/shop/pricetag.go @@ -15,7 +15,7 @@ type PriceTagOptions struct { } // PriceTag is a coin and amount; sales strike the old. -func PriceTag(c *ui.Context, price int, o PriceTagOptions) *ui.Element { +func PriceTag(c *ui.Context, price int, o PriceTagOptions) ui.Element { k := pixel.K(c) sp, word := coin(k, o.Currency) now, was := amount("PriceTag", price), amount("PriceTag Was", o.Was) diff --git a/shop/pricingtiers.go b/shop/pricingtiers.go index 9998772..73b78bb 100644 --- a/shop/pricingtiers.go +++ b/shop/pricingtiers.go @@ -23,7 +23,7 @@ type PricingTiersOptions struct { } type PricingTiersResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -57,7 +57,7 @@ func PricingTiers(c *ui.Context, text string, tiers [3]Tier, chosen *int, o Pric name, price := k.Msg(metals[i]), strconv.Itoa(t.Price) sel := *chosen // The shell draws the card; the radio stays bare. - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { return ui.RadioBase(c, &sel, i).Label(name + ", " + price + " " + word).Disabled(o.Disabled) }) // The radio picks before it is told it is off. diff --git a/shop/quantity.go b/shop/quantity.go index 30e333e..6fd4bfe 100644 --- a/shop/quantity.go +++ b/shop/quantity.go @@ -18,7 +18,7 @@ type QuantityOptions struct { } type QuantityResult struct { - Element *ui.Element + Element ui.Element Changed bool } @@ -29,7 +29,7 @@ func Quantity(c *ui.Context, text string, value *int, o QuantityOptions) Quantit if lo < 0 || hi < lo || *value < lo || *value > hi { panic(fmt.Sprintf("shop: Quantity %d outside %d to %d", *value, lo, hi)) } - 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.RoleStepper).Label(text).Disabled(o.Disabled) }) old, nw := *value, pixel.Measure(strconv.Itoa(hi))+6 diff --git a/shop/receipt.go b/shop/receipt.go index 13eb3be..15d8c21 100644 --- a/shop/receipt.go +++ b/shop/receipt.go @@ -27,7 +27,7 @@ type ReceiptOptions struct { } // Receipt prints lines dot-matrix style, perforated down the sides. -func Receipt(c *ui.Context, shop string, lines []ReceiptLine, o ReceiptOptions) *ui.Element { +func Receipt(c *ui.Context, shop string, lines []ReceiptLine, o ReceiptOptions) ui.Element { k := pixel.K(c) if shop == "" { panic("shop: Receipt has no shop name") diff --git a/shop/salebadge.go b/shop/salebadge.go index 864f7f6..406ac2a 100644 --- a/shop/salebadge.go +++ b/shop/salebadge.go @@ -114,7 +114,7 @@ var ( ) // SaleBadge is a SALE -n% starburst whose rim flashes. -func SaleBadge(c *ui.Context, percent int, o SaleBadgeOptions) *ui.Element { +func SaleBadge(c *ui.Context, percent int, o SaleBadgeOptions) ui.Element { k := pixel.K(c) if percent < 1 || percent > 99 { panic(fmt.Sprintf("shop: SaleBadge of %d%%", percent)) diff --git a/shop/shop.go b/shop/shop.go index 8523144..14b2095 100644 --- a/shop/shop.go +++ b/shop/shop.go @@ -39,7 +39,7 @@ func amount(name string, n int) string { } // money draws a currency icon and amount, named for readers. -func money(c *ui.Context, k pixel.Kit, cur Currency, n int, slot pixel.Slot) *ui.Element { +func money(c *ui.Context, k pixel.Kit, cur Currency, n int, slot pixel.Slot) ui.Element { sp, word := coin(k, cur) s := amount("money", n) return ui.Box(c).Size(k.D(14+pixel.Measure(s)), k.D(pixel.LineHeight)).Role(ui.RoleText).Label(s + " " + word).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/shop/shopitem.go b/shop/shopitem.go index 1ffc290..14d6779 100644 --- a/shop/shopitem.go +++ b/shop/shopitem.go @@ -29,7 +29,7 @@ type ShopItemOptions struct { } type ShopItemResult struct { - Element *ui.Element + Element ui.Element Bought bool } diff --git a/shop/tradewindow.go b/shop/tradewindow.go index 096b9ec..aab6bf6 100644 --- a/shop/tradewindow.go +++ b/shop/tradewindow.go @@ -24,7 +24,7 @@ type TradeWindowOptions struct { } type TradeWindowResult struct { - Element *ui.Element + Element ui.Element Changed bool // Traded reports both confirms landing this frame. Traded bool diff --git a/shop/vendingmachine.go b/shop/vendingmachine.go index b96651a..b916bba 100644 --- a/shop/vendingmachine.go +++ b/shop/vendingmachine.go @@ -26,7 +26,7 @@ type VendingMachineOptions struct { } type VendingMachineResult struct { - Element *ui.Element + Element ui.Element // Vended is the slot dropped this frame, else -1. Vended int } diff --git a/shop/wallet.go b/shop/wallet.go index 0211eb7..2536a33 100644 --- a/shop/wallet.go +++ b/shop/wallet.go @@ -13,7 +13,7 @@ type WalletOptions struct { } // Wallet shows gold and gem balances that roll on change. -func Wallet(c *ui.Context, gold, gems int, o WalletOptions) *ui.Element { +func Wallet(c *ui.Context, gold, gems int, o WalletOptions) ui.Element { k := pixel.K(c) g, m := amount("Wallet gold", gold), amount("Wallet gems", gems) _, gw := coin(k, Gold) diff --git a/tabletop/bingocard.go b/tabletop/bingocard.go index daffbdb..fc6eead 100644 --- a/tabletop/bingocard.go +++ b/tabletop/bingocard.go @@ -16,7 +16,7 @@ type BingoCardOptions struct { } type BingoCardResult struct { - Element *ui.Element + Element ui.Element // Bingo is the first full line, this frame. Changed, Bingo bool } diff --git a/tabletop/cardhand.go b/tabletop/cardhand.go index 4476d6f..3a0f273 100644 --- a/tabletop/cardhand.go +++ b/tabletop/cardhand.go @@ -15,7 +15,7 @@ type CardHandOptions struct { } type CardHandResult struct { - Element *ui.Element + Element ui.Element // Played is the card played this frame, else -1. Played int } @@ -49,7 +49,7 @@ func CardHand(c *ui.Context, text string, cards []Card, o CardHandOptions) CardH n := len(cards) w := max(cardW, fan*(n-1)+cardW) res.Element = col.Children(func() { - 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.RoleGroup).Label(text).Disabled(o.Disabled) }) *cur = max(0, min(*cur, n-1)) diff --git a/tabletop/chessboard.go b/tabletop/chessboard.go index b83689e..c389b72 100644 --- a/tabletop/chessboard.go +++ b/tabletop/chessboard.go @@ -39,7 +39,7 @@ type ChessboardOptions struct { } type ChessboardResult struct { - Element *ui.Element + Element ui.Element // Moved is this frame's move, From one square To another. Moved bool From, To int diff --git a/tabletop/coinflip.go b/tabletop/coinflip.go index 526a7cb..1e50758 100644 --- a/tabletop/coinflip.go +++ b/tabletop/coinflip.go @@ -23,7 +23,7 @@ var ( const coinUp = 18 // CoinFlip tosses a coin on a new Cue. -func CoinFlip(c *ui.Context, text string, heads bool, o CoinFlipOptions) *ui.Element { +func CoinFlip(c *ui.Context, text string, heads bool, o CoinFlipOptions) ui.Element { k := pixel.K(c) word := k.Msg("tails") if heads { diff --git a/tabletop/deck.go b/tabletop/deck.go index 9923217..4b495d5 100644 --- a/tabletop/deck.go +++ b/tabletop/deck.go @@ -15,7 +15,7 @@ type DeckOptions struct { } type DeckResult struct { - Element *ui.Element + Element ui.Element // Drawn is a card taken this frame. Drawn bool } @@ -28,7 +28,7 @@ func Deck(c *ui.Context, text string, count int, o DeckOptions) DeckResult { panic(fmt.Sprintf("tabletop: Deck of %d", count)) } left := fmt.Sprintf(k.Msg("cards_left"), count) - 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.RoleButton).Label(text).Description(left).Disabled(o.Disabled || count == 0) }) res := DeckResult{Element: shell, Drawn: !base.IsDisabled() && base.Clicked()} diff --git a/tabletop/dice.go b/tabletop/dice.go index 7725369..8cf9d0d 100644 --- a/tabletop/dice.go +++ b/tabletop/dice.go @@ -18,7 +18,7 @@ type DiceOptions struct { var hop = [...]int{0, 3, 5, 6, 6, 5, 3, 1} // Dice is a d6 showing value; new Cues tumble it. -func Dice(c *ui.Context, text string, value int, o DiceOptions) *ui.Element { +func Dice(c *ui.Context, text string, value int, o DiceOptions) ui.Element { k := pixel.K(c) if value < 1 || value > 6 { panic(fmt.Sprintf("tabletop: Dice value %d", value)) diff --git a/tabletop/dicetray.go b/tabletop/dicetray.go index d422e45..ad4574d 100644 --- a/tabletop/dicetray.go +++ b/tabletop/dicetray.go @@ -14,7 +14,7 @@ type DiceTrayOptions struct { } // DiceTray holds several dice on felt, and their total. -func DiceTray(c *ui.Context, text string, values []int, o DiceTrayOptions) *ui.Element { +func DiceTray(c *ui.Context, text string, values []int, o DiceTrayOptions) ui.Element { k := pixel.K(c) if len(values) == 0 { panic("tabletop: DiceTray has no dice") diff --git a/tabletop/domino.go b/tabletop/domino.go index c4750c4..ee0dde2 100644 --- a/tabletop/domino.go +++ b/tabletop/domino.go @@ -14,7 +14,7 @@ type DominoOptions struct { } // Domino is a tile of two halves of 0–6 pips. -func Domino(c *ui.Context, a, b int, o DominoOptions) *ui.Element { +func Domino(c *ui.Context, a, b int, o DominoOptions) ui.Element { k := pixel.K(c) if a < 0 || a > 6 || b < 0 || b > 6 { panic(fmt.Sprintf("tabletop: Domino %d|%d", a, b)) diff --git a/tabletop/goboard.go b/tabletop/goboard.go index 749403b..ffa5f8a 100644 --- a/tabletop/goboard.go +++ b/tabletop/goboard.go @@ -23,7 +23,7 @@ type GoBoardOptions struct { } type GoBoardResult struct { - Element *ui.Element + Element ui.Element // Placed is the point played this frame, else -1. Placed int } diff --git a/tabletop/mahjongtile.go b/tabletop/mahjongtile.go index f143807..136699f 100644 --- a/tabletop/mahjongtile.go +++ b/tabletop/mahjongtile.go @@ -51,7 +51,7 @@ var ( const tileW, tileH = 26, 34 // MahjongTile is a suit or honor tile, or its back. -func MahjongTile(c *ui.Context, t Tile, o MahjongTileOptions) *ui.Element { +func MahjongTile(c *ui.Context, t Tile, o MahjongTileOptions) ui.Element { k := pixel.K(c) if t.Family < Dots || t.Family > Dragons || t.Rank < 1 || t.Rank > families[t.Family].ranks { panic(fmt.Sprintf("tabletop: MahjongTile %+v", t)) diff --git a/tabletop/meeple.go b/tabletop/meeple.go index 46d5bd0..bc3e28e 100644 --- a/tabletop/meeple.go +++ b/tabletop/meeple.go @@ -18,7 +18,7 @@ type MeepleOptions struct { } // Meeple is a player's token, standing in its slot. -func Meeple(c *ui.Context, text string, o MeepleOptions) *ui.Element { +func Meeple(c *ui.Context, text string, o MeepleOptions) ui.Element { k := pixel.K(c) if o.Player < 0 || o.Player >= len(players) { panic(fmt.Sprintf("tabletop: Meeple player %d", o.Player)) diff --git a/tabletop/playingcard.go b/tabletop/playingcard.go index 692d2eb..334f57a 100644 --- a/tabletop/playingcard.go +++ b/tabletop/playingcard.go @@ -12,7 +12,7 @@ type PlayingCardOptions struct { } // PlayingCard is a card: rank and suit, or its back. -func PlayingCard(c *ui.Context, cd Card, o PlayingCardOptions) *ui.Element { +func PlayingCard(c *ui.Context, cd Card, o PlayingCardOptions) ui.Element { k := pixel.K(c) cd.check("PlayingCard") name := cd.name(k) diff --git a/tabletop/roulette.go b/tabletop/roulette.go index 4417432..bb32e27 100644 --- a/tabletop/roulette.go +++ b/tabletop/roulette.go @@ -18,7 +18,7 @@ type RouletteOptions struct { const wheel = 28 // Roulette spins its wheel, on a new Cue, to result. -func Roulette(c *ui.Context, text string, segments []string, result int, o RouletteOptions) *ui.Element { +func Roulette(c *ui.Context, text string, segments []string, result int, o RouletteOptions) ui.Element { k := pixel.K(c) n := len(segments) if n < 2 || n > 64 || result < 0 || result >= n || slices.Contains(segments, "") { diff --git a/tabletop/scoretrack.go b/tabletop/scoretrack.go index 822a98f..90cf313 100644 --- a/tabletop/scoretrack.go +++ b/tabletop/scoretrack.go @@ -19,7 +19,7 @@ type ScoreTrackOptions struct { const space = 20 // ScoreTrack winds a path; player meeples mark their scores. -func ScoreTrack(c *ui.Context, text string, scores []int, o ScoreTrackOptions) *ui.Element { +func ScoreTrack(c *ui.Context, text string, scores []int, o ScoreTrackOptions) ui.Element { k := pixel.K(c) n := pixel.Or("ScoreTrack Spaces", o.Spaces, 30) // A space prints three digits at most. diff --git a/tabletop/tictactoe.go b/tabletop/tictactoe.go index c214c38..563efb9 100644 --- a/tabletop/tictactoe.go +++ b/tabletop/tictactoe.go @@ -13,7 +13,7 @@ type TicTacToeOptions struct { } type TicTacToeResult struct { - Element *ui.Element + Element ui.Element // Played is the cell you took this frame, else -1. Played int // Over is the game ending this frame; Restarted, the CONTINUE. diff --git a/text/blinktext.go b/text/blinktext.go index 32909b5..8c434b8 100644 --- a/text/blinktext.go +++ b/text/blinktext.go @@ -14,7 +14,7 @@ type BlinkTextOptions struct { } // BlinkText blinks; under Reduce Motion it stays shown. -func BlinkText(c *ui.Context, s string, o BlinkTextOptions) *ui.Element { +func BlinkText(c *ui.Context, s string, o BlinkTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) fps := pixel.Or("BlinkText FPS", o.FPS, 4) return block(c, k, s, pixel.Measure(s)*sc, pixel.LineHeight*sc, func(g pixel.Canvas) { diff --git a/text/code.go b/text/code.go index 95cf030..f24dcb4 100644 --- a/text/code.go +++ b/text/code.go @@ -11,7 +11,7 @@ type CodeOptions struct { } // Code is an inline chip on Well, bordered in Edge. -func Code(c *ui.Context, s string, o CodeOptions) *ui.Element { +func Code(c *ui.Context, s string, o CodeOptions) ui.Element { k := pixel.K(c) return block(c, k, s, pixel.Measure(s)+6, pixel.LineHeight+2, func(g pixel.Canvas) { g.Frame(0, 0, g.W, g.H, pixel.Flat, pixel.Well) diff --git a/text/gradienttext.go b/text/gradienttext.go index 8b2b1dc..d96fa26 100644 --- a/text/gradienttext.go +++ b/text/gradienttext.go @@ -13,7 +13,7 @@ type GradientTextOptions struct { } // GradientText dithers glyph dots from Top down to Bottom. -func GradientText(c *ui.Context, s string, o GradientTextOptions) *ui.Element { +func GradientText(c *ui.Context, s string, o GradientTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) top, bottom := slotOr(o.Top, pixel.AccentLit), slotOr(o.Bottom, pixel.Accent) return block(c, k, s, pixel.Measure(s)*sc, pixel.LineHeight*sc, func(g pixel.Canvas) { diff --git a/text/heading.go b/text/heading.go index 01260cb..9e55493 100644 --- a/text/heading.go +++ b/text/heading.go @@ -15,7 +15,7 @@ type HeadingOptions struct { } // Heading is a title with a 1-dot Shade shadow. -func Heading(c *ui.Context, s string, o HeadingOptions) *ui.Element { +func Heading(c *ui.Context, s string, o HeadingOptions) ui.Element { level := pixel.Or("Heading Level", o.Level, 1) if level > 3 { panic(fmt.Sprintf("text: Heading Level %d outside 1–3", level)) diff --git a/text/highlight.go b/text/highlight.go index b1bb1b1..7d2ac3e 100644 --- a/text/highlight.go +++ b/text/highlight.go @@ -15,7 +15,7 @@ type HighlightOptions struct { } // Highlight marks each match of query in s, ignoring case. -func Highlight(c *ui.Context, s, query string, o HighlightOptions) *ui.Element { +func Highlight(c *ui.Context, s, query string, o HighlightOptions) ui.Element { k := pixel.K(c) mark := slotOr(o.Mark, pixel.Warn) rs := []rune(s) diff --git a/text/kbd.go b/text/kbd.go index 84bac12..dca6c18 100644 --- a/text/kbd.go +++ b/text/kbd.go @@ -11,7 +11,7 @@ type KbdOptions struct { } // Kbd is a keycap with depth; Pressed sinks it. -func Kbd(c *ui.Context, label string, o KbdOptions) *ui.Element { +func Kbd(c *ui.Context, label string, o KbdOptions) ui.Element { k := pixel.K(c) face := pixel.LineHeight + 2 return block(c, k, label, pixel.Measure(label)+10, face+3, func(g pixel.Canvas) { diff --git a/text/link.go b/text/link.go index 191e5bc..ae74d21 100644 --- a/text/link.go +++ b/text/link.go @@ -13,14 +13,14 @@ type LinkOptions struct { } type LinkResult struct { - Element *ui.Element + Element ui.Element Clicked bool } // Link is underlined text opening url on click or Enter. func Link(c *ui.Context, label, url string, o LinkOptions) LinkResult { k := pixel.K(c) - 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(label).Description(url).Disabled(o.Disabled).Cursor(ui.CursorPointer) }) w := pixel.Measure(label) diff --git a/text/marquee.go b/text/marquee.go index fe6957a..45376e5 100644 --- a/text/marquee.go +++ b/text/marquee.go @@ -17,7 +17,7 @@ type marqueeKey struct{} type marqueeState struct{ offset, last int } // Marquee scrolls s a dot a step; hover pauses it. -func Marquee(c *ui.Context, s string, o MarqueeOptions) *ui.Element { +func Marquee(c *ui.Context, s string, o MarqueeOptions) ui.Element { k := pixel.K(c) w := pixel.Or("Marquee Width", o.Width, 120) e := ui.Box(c).Size(k.D(w), k.D(pixel.LineHeight)).Clip().Label(s).Role(ui.RoleText) diff --git a/text/microtext.go b/text/microtext.go index 799cd8d..47d2d9f 100644 --- a/text/microtext.go +++ b/text/microtext.go @@ -14,7 +14,7 @@ type MicroTextOptions struct { } // MicroText draws s upper-cased in micro font; unknown runes panic. -func MicroText(c *ui.Context, s string, o MicroTextOptions) *ui.Element { +func MicroText(c *ui.Context, s string, o MicroTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) up := strings.ToUpper(s) return block(c, k, s, pixel.MicroWidth(up)*sc, pixel.MicroHeight*sc, func(g pixel.Canvas) { diff --git a/text/odometer.go b/text/odometer.go index 6351e9b..0c112fd 100644 --- a/text/odometer.go +++ b/text/odometer.go @@ -26,7 +26,7 @@ type odoState struct { const odoCell = 10 // Odometer rolls its digits toward value, step by step. -func Odometer(c *ui.Context, value int, o OdometerOptions) *ui.Element { +func Odometer(c *ui.Context, value int, o OdometerOptions) ui.Element { if value < 0 { panic(fmt.Sprintf("text: Odometer value %d is negative", value)) } diff --git a/text/outlinetext.go b/text/outlinetext.go index 58aaba9..f2daede 100644 --- a/text/outlinetext.go +++ b/text/outlinetext.go @@ -13,7 +13,7 @@ type OutlineTextOptions struct { } // OutlineText rings every glyph dot of s with Edge. -func OutlineText(c *ui.Context, s string, o OutlineTextOptions) *ui.Element { +func OutlineText(c *ui.Context, s string, o OutlineTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) fill := slotOr(o.Slot, pixel.Lit) return block(c, k, s, pixel.Measure(s)*sc+2, pixel.LineHeight*sc+2, func(g pixel.Canvas) { diff --git a/text/paragraph.go b/text/paragraph.go index a1405b3..bc6f782 100644 --- a/text/paragraph.go +++ b/text/paragraph.go @@ -25,7 +25,7 @@ type ParagraphOptions struct { } // Paragraph wraps s at the width it had last frame. -func Paragraph(c *ui.Context, s string, o ParagraphOptions) *ui.Element { +func Paragraph(c *ui.Context, s string, o ParagraphOptions) ui.Element { k := pixel.K(c) gap := pixel.Or("Paragraph Gap", o.Gap, 0) if o.Align > Right { diff --git a/text/quote.go b/text/quote.go index 8bb8057..c37635b 100644 --- a/text/quote.go +++ b/text/quote.go @@ -18,7 +18,7 @@ type QuoteOptions struct { } // Quote is a block quote with bar, mark and cite. -func Quote(c *ui.Context, s string, o QuoteOptions) *ui.Element { +func Quote(c *ui.Context, s string, o QuoteOptions) ui.Element { k := pixel.K(c) return ui.Row(c).Gap(k.D(6)).AlignItems(ui.Stretch).Role(ui.RoleGroup).Children(func() { ui.Box(c).Width(k.D(2)).Draw(func(p *ui.Painter, r ui.Rect) { diff --git a/text/rainbowtext.go b/text/rainbowtext.go index 576d0ab..4a473d7 100644 --- a/text/rainbowtext.go +++ b/text/rainbowtext.go @@ -15,7 +15,7 @@ type RainbowTextOptions struct { } // RainbowText gives each character the next slot of a band. -func RainbowText(c *ui.Context, s string, o RainbowTextOptions) *ui.Element { +func RainbowText(c *ui.Context, s string, o RainbowTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) band := o.Band if len(band) == 0 { diff --git a/text/scrambletext.go b/text/scrambletext.go index b8584c0..c2a3dcd 100644 --- a/text/scrambletext.go +++ b/text/scrambletext.go @@ -23,7 +23,7 @@ type scrambleState struct { } // ScrambleText settles from random glyphs to s, left to right. -func ScrambleText(c *ui.Context, s string, o ScrambleTextOptions) *ui.Element { +func ScrambleText(c *ui.Context, s string, o ScrambleTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) fps := pixel.Or("ScrambleText FPS", o.FPS, 15) rs := []rune(s) diff --git a/text/sevensegment.go b/text/sevensegment.go index 10513eb..9fe59f0 100644 --- a/text/sevensegment.go +++ b/text/sevensegment.go @@ -24,7 +24,7 @@ type SevenSegmentOptions struct { } // SevenSegment shows digits, minus and colon with ghost segments. -func SevenSegment(c *ui.Context, s string, o SevenSegmentOptions) *ui.Element { +func SevenSegment(c *ui.Context, s string, o SevenSegmentOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) ink := slotOr(o.Slot, pixel.Bad) w := 0 diff --git a/text/shadowtext.go b/text/shadowtext.go index c172ba1..b874085 100644 --- a/text/shadowtext.go +++ b/text/shadowtext.go @@ -12,7 +12,7 @@ type ShadowTextOptions struct { } // ShadowText drops a 1-dot Shade shadow behind s. -func ShadowText(c *ui.Context, s string, o ShadowTextOptions) *ui.Element { +func ShadowText(c *ui.Context, s string, o ShadowTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) return block(c, k, s, pixel.Measure(s)*sc+1, pixel.LineHeight*sc+1, func(g pixel.Canvas) { g.Text(1, 1, s, pixel.Shade, sc) diff --git a/text/shaketext.go b/text/shaketext.go index ff2719b..b74be24 100644 --- a/text/shaketext.go +++ b/text/shaketext.go @@ -13,7 +13,7 @@ type ShakeTextOptions struct { } // ShakeText jitters each character a dot, seeded, at 12 fps. -func ShakeText(c *ui.Context, s string, o ShakeTextOptions) *ui.Element { +func ShakeText(c *ui.Context, s string, o ShakeTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) return block(c, k, s, pixel.Measure(s)*sc+2, pixel.LineHeight*sc+2, func(g pixel.Canvas) { step := g.Step(12) diff --git a/text/text.go b/text/text.go index 112142d..1290699 100644 --- a/text/text.go +++ b/text/text.go @@ -10,7 +10,7 @@ import ( ) // block is a w×h dot text box that fn draws. -func block(c *ui.Context, k pixel.Kit, s string, w, h int, fn func(g pixel.Canvas)) *ui.Element { +func block(c *ui.Context, k pixel.Kit, s string, w, h int, fn func(g pixel.Canvas)) ui.Element { return ui.Box(c).Size(k.D(w), k.D(h)).Label(s).Role(ui.RoleText).Draw(func(p *ui.Painter, r ui.Rect) { fn(k.Canvas(p, r)) }) diff --git a/text/typewriter.go b/text/typewriter.go index 2e39535..60355c9 100644 --- a/text/typewriter.go +++ b/text/typewriter.go @@ -23,7 +23,7 @@ type TypewriterOptions struct { } type TypewriterResult struct { - Element *ui.Element + Element ui.Element // Done is true in the frame the whole text shows. Done bool } @@ -45,7 +45,7 @@ func Typewriter(c *ui.Context, s string, o TypewriterOptions) TypewriterResult { } // S5 steps at 4–15 fps; fast typing shows more. fps := max(4, min(cps, 15)) - shell, base, st := pixel.Shell(c, func() *ui.Element { + shell, base, st := pixel.Shell(c, func() ui.Element { e := ui.Box(c).Role(ui.RoleText).Label(s).Disabled(o.Disabled) if !o.Disabled { e.Focusable() diff --git a/text/wavetext.go b/text/wavetext.go index f6f78f2..becca72 100644 --- a/text/wavetext.go +++ b/text/wavetext.go @@ -16,7 +16,7 @@ type WaveTextOptions struct { } // WaveText bobs characters on a sine wave at 10 fps. -func WaveText(c *ui.Context, s string, o WaveTextOptions) *ui.Element { +func WaveText(c *ui.Context, s string, o WaveTextOptions) ui.Element { k, sc := pixel.K(c), scale(o.Scale) amp := pixel.Or("WaveText Amp", o.Amp, 2) return block(c, k, s, pixel.Measure(s)*sc, pixel.LineHeight*sc+2*amp, func(g pixel.Canvas) {