Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **examples/sounds** — system sounds demo with checkbox, radio, slider, and button interactions using platform-native `gogpu/sound`. Demonstrates `sound.Play(sound.Click)` for UI feedback.

### Fixed

- **fix(textfield): cursor rounding** ([#211](https://github.com/gogpu/ui/issues/211)) — `CursorX` base X now rounded with `math.Round` to match `DrawText`'s pixel-grid rounding. Eliminates constant cursor-to-text offset (up to 0.5px).
- **fix(textfield): horizontal scroll** ([#212](https://github.com/gogpu/ui/issues/212)) — TextField scrolls text horizontally when content exceeds field width. `ensureCursorVisible()` keeps cursor within visible area (Flutter `_showCaretOnScreen` pattern). `TextRect` in PaintState separates text drawing origin from clip rect.
- **fix(textfield): cursor-scroll sync** — cursor position computed in text-local coordinates, `scrollOffsetX` applied uniformly to cursor/selection/text. Matches Flutter `_paintOffset` and Qt `topLeft` patterns. Fixes cursor drifting leftward during scroll.

### Changed

- **refactor(widget): decouple widget/ from gg/scene** (ADR-036 Phase 1) — replaced `*scene.Scene` with `SceneCache` interface in widget package. Third-party widget authors importing `widget/` + `geometry/` + `event/` now compile **71 packages instead of 124** (zero gg/wgpu/naga in the dependency chain). `SceneFactory` registration pattern follows existing `SceneRecorder` DI.
Expand All @@ -15,7 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Changed: `WidgetBase.CachedScene()` / `SetCachedScene()` types → `SceneCache`
- Changed: `SceneRecorder` function signature uses `SceneCache`
- 92 files changed, rendering layer uses type assertions where concrete `*scene.Scene` needed
- **deps:** gg v0.50.11 → v0.50.12, gogpu v0.48.5 → v0.50.0, wgpu v0.30.35 → v0.30.36
- **deps:** gg v0.50.11 → v0.50.13, gogpu v0.48.5 → v0.50.0, wgpu v0.30.35 → v0.30.36
- **gg v0.50.13:** `Face.Advance()` returns hinted advances matching `drawGlyphs` — fixes cursor drift on wide Cyrillic glyphs (gg#479).
- **gogpu v0.50.0:** outgoing drag-and-drop, per-pixel-alpha transparency, window Show/Hide/SetPosition/SetSize API, macOS menu Role+Action fix.
- **wgpu v0.30.36:** Vulkan present semaphore fix.

Expand Down
117 changes: 117 additions & 0 deletions examples/sounds/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"fmt"
"log"

_ "github.com/gogpu/gg/gpu"

"github.com/gogpu/gogpu"
"github.com/gogpu/gogpu/sound"
"github.com/gogpu/ui/app"
"github.com/gogpu/ui/core/button"
"github.com/gogpu/ui/core/checkbox"
"github.com/gogpu/ui/core/radio"
"github.com/gogpu/ui/core/slider"
"github.com/gogpu/ui/desktop"
"github.com/gogpu/ui/primitives"
"github.com/gogpu/ui/theme/material3"
"github.com/gogpu/ui/widget"
)

func main() {
sound.SetEnabled(true)

m3 := material3.New(widget.Hex(0x1565C0))
bp := material3.ButtonPainter{Theme: m3}
cp := material3.CheckboxPainter{Theme: m3}
rp := material3.RadioPainter{Theme: m3}
sp := material3.SliderPainter{Theme: m3}

root := primitives.VBox(
primitives.Text("System Sounds Demo").FontSize(22).Bold(),
primitives.Text("Every interaction plays a platform system sound").
FontSize(13).Color(widget.RGBA(0.5, 0.5, 0.5, 1)),

primitives.Text("Checkboxes").FontSize(16).Bold(),
checkbox.New(
checkbox.LabelOpt("Enable notifications"),
checkbox.OnToggle(func(checked bool) {
sound.Play(sound.Click)
fmt.Println("notifications:", checked)
}),
checkbox.PainterOpt(cp),
),
checkbox.New(
checkbox.LabelOpt("Dark mode"),
checkbox.OnToggle(func(checked bool) {
sound.Play(sound.Click)
fmt.Println("dark mode:", checked)
}),
checkbox.PainterOpt(cp),
),

primitives.Text("Theme").FontSize(16).Bold(),
radio.NewGroup(
radio.Items(
radio.ItemDef{Value: "light", Label: "Light"},
radio.ItemDef{Value: "dark", Label: "Dark"},
radio.ItemDef{Value: "system", Label: "System"},
),
radio.Selected("light"),
radio.OnChange(func(v string) {
sound.Play(sound.Click)
fmt.Println("theme:", v)
}),
radio.GroupPainter(rp),
),

primitives.Text("Volume").FontSize(16).Bold(),
slider.New(
slider.Min(0),
slider.Max(100),
slider.Value(50),
slider.OnChange(func(v float32) {
fmt.Printf("volume: %.0f%%\n", v)
}),
slider.PainterOpt(sp),
),

primitives.HBox(
button.New(
button.Text("Success"),
button.OnClick(func() { sound.Play(sound.Success) }),
button.PainterOpt(bp),
button.VariantOpt(button.Filled),
),
button.New(
button.Text("Warning"),
button.OnClick(func() { sound.Play(sound.Warning) }),
button.PainterOpt(bp),
button.VariantOpt(button.Tonal),
),
button.New(
button.Text("Error"),
button.OnClick(func() { sound.Play(sound.Error) }),
button.PainterOpt(bp),
button.VariantOpt(button.Outlined),
),
).Gap(8),
).Padding(24).Gap(12)

gogpuApp := gogpu.NewApp(gogpu.DefaultConfig().
WithTitle("gogpu/ui — System Sounds").
WithSize(400, 520))

uiApp := app.New(
app.WithWindowProvider(gogpuApp),
app.WithPlatformProvider(gogpuApp),
app.WithEventSource(gogpuApp.EventSource()),
app.WithTheme(m3.AsTheme()),
)
uiApp.SetRoot(root)

if err := desktop.Run(gogpuApp, uiApp); err != nil {
log.Fatal(err)
}
}
Loading