| name | ux-ui-troubleshooter |
| description | Comprehensive guide for creating gio/ux UIs. Invoke when developing any gio/ux UI, creating widgets, layouts, or troubleshooting panics and compilation errors. |
Gio/UX UI Development Guide
Complete guide for creating UIs with the gio/ux widget library. Use this skill when developing any gio/ux interface.
Development Workflow
When creating a new UI, follow this workflow to explore and select appropriate widgets:
1. Explore Examples Directory
First, check D:\็ฌ่ฎฐๆฌ\p\ux\examples\ for similar UI patterns:
D:\็ฌ่ฎฐๆฌ\p\ux\examples\
โโโ designer/ # Full UI designer app - comprehensive widget usage
โโโ simple/ # Simple examples
โโโ hypedbg/ # HyperDbg specific examples
โโโ walker/ # PDB parsing demos
โโโ x64dbg/ # Debugger UI demos (IMPORTANT: contains treetable, symbol, disassembly examples)
Action: Read example files to understand:
- How widgets are initialized
- Layout patterns used
- Event handling patterns
- Integration with business logic
2. Explore Widget Directory
Check D:\็ฌ่ฎฐๆฌ\p\ux\widget\ for available widgets and their APIs:
D:\็ฌ่ฎฐๆฌ\p\ux\widget\
โโโ button/ # Button variants
โโโ input/ # Text input widgets
โโโ card/ # Card containers
โโโ chip/ # Chips and tags
โโโ logview/ # Log viewer
โโโ menu/ # Menus and dropdowns
โโโ dialog/ # Dialogs
โโโ slider/ # Sliders
โโโ switch/ # Toggle switches
โโโ checkbox/ # Checkboxes
โโโ radiobutton/ # Radio buttons
โโโ progress/ # Progress indicators
โโโ tab/ # Tab navigation
โโโ treetable/ # TreeTable widget (IMPORTANT: for large data display)
โโโ table/ # Table views
โโโ tree/ # Tree views
โโโ fab/ # Floating action buttons
โโโ navigationrail/ # Navigation rail
โโโ navigationbar/ # Bottom navigation
โโโ appbar/ # App bars
โโโ datepicker/ # Date picker
โโโ timepicker/ # Time picker
โโโ ... # More widgets
3. Explore App Directory
Check D:\็ฌ่ฎฐๆฌ\p\ux\app\ for application lifecycle:
D:\็ฌ่ฎฐๆฌ\p\ux\app\
โโโ debug.go # Main application entry and utilities
Key functions:
app.Run(title, renderFunc) - Start application
app.RunWithConfig(config, renderFunc) - Start with custom config
app.RequestRedraw() - Request UI redraw from background thread
4. Explore WDK Directory
Check D:\็ฌ่ฎฐๆฌ\p\ux\wdk\ for utilities:
D:\็ฌ่ฎฐๆฌ\p\ux\wdk\
โโโ editor.go # Text editor widget
โโโ theme.go # Theme access
โโโ icons.go # Icon handling
โโโ text.go # Text rendering
โโโ ...
Critical: TreeTable Usage Pattern
This is the MOST IMPORTANT widget for displaying large datasets (functions, symbols, disassembly, etc.)
Correct TreeTable Usage
type FuncRow struct {
Name string `table:"Name"`
Address string `table:"Address"`
Size string `table:"Size"`
}
table := treetable.NewTreeTable[FuncRow]()
table.AirTable.TableContext = treetable.TableContext{
SetRootRowsCallBack: func() {},
JsonName: "Functions",
}
func (v *MyViewer) Update(gtx layout.Context) {
v.dataMu.Lock()
if v.dataReady {
v.dataReady = false
rows := v.pendingData
v.dataMu.Unlock()
table.Root().SetChildren(nil)
for _, row := range rows {
table.Root().AddChild(table.NewNode(row))
}
table.AirTable.Refresh()
} else {
v.dataMu.Unlock()
}
}
Background Data Loading Pattern
type Viewer struct {
table *treetable.TreeTable[RowType]
pendingData []RowType
dataReady bool
dataMu sync.Mutex
loading bool
loadingMu sync.Mutex
}
func (v *Viewer) loadDataAsync() {
v.loadingMu.Lock()
if v.loading {
v.loadingMu.Unlock()
return
}
v.loading = true
v.loadingMu.Unlock()
defer func() {
v.loadingMu.Lock()
v.loading = false
v.loadingMu.Unlock()
app.RequestRedraw()
}()
data := doHeavyWork()
v.dataMu.Lock()
v.pendingData = data
v.dataReady = true
v.dataMu.Unlock()
}
DO NOT DO THIS
- โ Modifying table data directly in goroutine
- โ Calling
table.Root().AddChild() in background thread
- โ Forgetting
app.RequestRedraw() after background work
- โ Using
list.List for large datasets (causes UI lag)
DO THIS
- โ
Store pending data in goroutine, process in Update()
- โ
Use
sync.Mutex for thread-safe data sharing
- โ
Call
app.RequestRedraw() to trigger UI refresh
- โ
Use
treetable.TreeTable for large datasets
Widget Selection Guide
| Need | Widget | Factory Function |
|---|
| Large data display | treetable | treetable.NewTreeTable[T]() |
| Text input | input | input.FilledTextInput() |
| Multi-line text | input | input.FilledTextArea() |
| Compact input | input | input.CompactInput() |
| Primary action | button | button.Filled() |
| Secondary action | button | button.Elevated() |
| Tertiary action | button | button.Outlined() |
| Text only action | button | button.Text() |
| Container | card | &card.Card{Kind: card.Elevated} |
| Status indicator | chip | chip.Assist() |
| Filter option | chip | chip.Filter() |
| Log output | logview | logview.New() |
| Numeric input | slider | slider.StandardSlider() |
| Toggle option | switch | switch.New() |
| Multi-select | checkbox | checkbox.New() |
| Single-select | radiobutton | radiobutton.New() |
| Navigation | navigationrail | navigationrail.New() |
| Tabs | tab | tab.New() |
| Tree data | tree | tree.New() |
| Progress | progress | progress.New() |
| Dialog | dialog | dialog.New() |
| Menu | menu | menu.New() |
| Date input | datepicker | datepicker.New() |
| Time input | timepicker | timepicker.New() |
| Floating action | fab | fab.New() |
Quick Start Template
package main
import (
"image"
"sync"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"github.com/ddkwork/ux/app"
"github.com/ddkwork/ux/wdk"
"github.com/ddkwork/ux/widget/button"
"github.com/ddkwork/ux/widget/input"
"github.com/ddkwork/ux/widget/treetable"
)
type RowData struct {
Name string `table:"Name"`
Value string `table:"Value"`
}
type MyUI struct {
table *treetable.TreeTable[RowData]
loadButton *button.Button
input *input.Input
loading bool
loadingMu sync.Mutex
pendingData []RowData
dataReady bool
dataMu sync.Mutex
}
func NewMyUI() *MyUI {
ui := &MyUI{
table: treetable.NewTreeTable[RowData](),
loadButton: button.Filled(),
input: input.FilledTextInput(),
}
ui.table.AirTable.TableContext = treetable.TableContext{
SetRootRowsCallBack: func() {},
JsonName: "Data",
}
return ui
}
func (ui *MyUI) Update(gtx layout.Context) {
if ui.loadButton.Clicked(gtx) {
go ui.loadDataAsync()
}
ui.dataMu.Lock()
if ui.dataReady {
ui.dataReady = false
rows := ui.pendingData
ui.dataMu.Unlock()
ui.table.Root().SetChildren(nil)
for _, row := range rows {
ui.table.Root().AddChild(ui.table.NewNode(row))
}
ui.table.AirTable.Refresh()
} else {
ui.dataMu.Unlock()
}
}
func (ui *MyUI) loadDataAsync() {
ui.loadingMu.Lock()
if ui.loading {
ui.loadingMu.Unlock()
return
}
ui.loading = true
ui.loadingMu.Unlock()
defer func() {
ui.loadingMu.Lock()
ui.loading = false
ui.loadingMu.Unlock()
app.RequestRedraw()
}()
data := []RowData{{Name: "Item1", Value: "Value1"}}
ui.dataMu.Lock()
ui.pendingData = data
ui.dataReady = true
ui.dataMu.Unlock()
}
func (ui *MyUI) Layout(gtx layout.Context) layout.Dimensions {
materialTheme := wdk.GetMaterialTheme(gtx)
paint.FillShape(gtx.Ops, materialTheme.Scheme.Background.Color.AsNRGBA(),
clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
label := "Load"
ui.loadingMu.Lock()
if ui.loading {
label = "Loading..."
}
ui.loadingMu.Unlock()
return ui.loadButton.Layout(gtx, label)
}),
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return ui.table.AirTable.Layout(gtx)
}),
)
}
func main() {
ui := NewMyUI()
app.Run("My App", func(gtx layout.Context) {
ui.Update(gtx)
ui.Layout(gtx)
})
}
Widget Creation Reference
TreeTable (IMPORTANT!)
type MyRow struct {
Column1 string `table:"Column 1"`
Column2 string `table:"Column 2"`
}
table := treetable.NewTreeTable[MyRow]()
table.AirTable.TableContext = treetable.TableContext{
SetRootRowsCallBack: func() {},
JsonName: "MyTable",
}
table.Root().SetChildren(nil)
table.Root().AddChild(table.NewNode(MyRow{Column1: "value1", Column2: "value2"}))
table.AirTable.Refresh()
Input Widgets
inputWidget := input.FilledTextInput()
inputWidget.LabelText = "Label"
inputWidget.Editor.SetText("default value")
text := inputWidget.Editor.GetText()
textArea := input.FilledTextArea()
compactInput := input.CompactInput()
Buttons
filledBtn := button.Filled()
elevatedBtn := button.Elevated()
outlinedBtn := button.Outlined()
textBtn := button.Text()
tonalBtn := button.FilledTonal()
filledBtn.Layout(gtx, "Button Text")
if filledBtn.Clicked(gtx) {
}
Chips
assistChip := chip.Assist()
actionChip := chip.Action()
filterChip := chip.Filter()
assistChip.Layout(gtx, "Chip Label")
Cards
c := &card.Card{}
c.Kind = card.Elevated
return c.Layout(gtx, card.Content(func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
})
}))
LogView
log := logview.New()
log.Info("message")
log.Warn("warning")
log.Error("error")
log.Debugf("format: %s", value)
Icons
icon := wdk.RequireIconWidget(icons.HardwareMemory)
materialTheme := wdk.GetMaterialTheme(gtx)
icon(gtx, materialTheme.Scheme.Background.OnColor)
Layout Patterns
Vertical Layout
layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
}),
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
}),
)
Horizontal Layout
layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
}),
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
}),
)
Spacing & Padding
layout.Rigid(layout.Spacer{Width: unit.Dp(16)}.Layout)
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout)
layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
})
Theme & Colors
materialTheme := wdk.GetMaterialTheme(gtx)
materialTheme.Scheme.Primary.Color
materialTheme.Scheme.Primary.OnColor
materialTheme.Scheme.Background.Color
materialTheme.Scheme.Background.OnColor
materialTheme.Scheme.Error.Color
materialTheme.Scheme.Surface.Color
Text Labels
wdk.TitleL(gtx, "Large Title")
wdk.TitleM(gtx, "Medium Title")
wdk.TitleS(gtx, "Small Title")
wdk.LabelL(gtx, "Large Label")
wdk.LabelM(gtx, "Medium Label")
wdk.LabelS(gtx, "Small Label")
Code Search Strategy
Finding Examples
-
Search by widget name:
Grep pattern: treetable\.NewTreeTable
path: d:\็ฌ่ฎฐๆฌ\p\ux\examples
-
Search by pattern:
Grep pattern: func.*Update\(\).*error
path: d:\็ฌ่ฎฐๆฌ\p\ux\examples\x64dbg
-
Search for background loading:
Grep pattern: go func|goroutine|async
path: d:\็ฌ่ฎฐๆฌ\p\ux\examples\x64dbg
-
Search for RequestRedraw:
Grep pattern: RequestRedraw
path: d:\็ฌ่ฎฐๆฌ\p\ux
-
Search for mutex patterns:
Grep pattern: sync\.Mutex|loadingMu|dataMu
path: d:\็ฌ่ฎฐๆฌ\p\ux\examples
Key Files to Reference
d:\็ฌ่ฎฐๆฌ\p\ux\examples\x64dbg\debugger\symbol\symbol.go - TreeTable + background loading pattern
d:\็ฌ่ฎฐๆฌ\p\ux\examples\x64dbg\debugger\disassembly\disassembly.go - TreeTable with complex data
d:\็ฌ่ฎฐๆฌ\p\ux\examples\x64dbg\debugger\debugger.go - UI update pattern
d:\็ฌ่ฎฐๆฌ\p\ux\widget\treetable\table.go - TreeTable implementation
Common Pitfalls & Solutions
1. TreeTable Data Not Showing
Problem: Background thread modifies table but UI doesn't update
Solution:
- Store pending data in goroutine
- Process in Update() method (main thread)
- Call
app.RequestRedraw() after goroutine completes
2. UI Lag with Large Data
Problem: List widget causes lag with thousands of items
Solution: Use treetable.TreeTable instead of list.List
3. Input Widget Panic
Wrong: &input.Input{Editor: wdk.NewEditor(true, false)}
Correct: input.FilledTextInput()
Input requires contextArea and contextMenu to be initialized. Always use factory functions.
4. Editor Arguments
Wrong: wdk.NewEditor()
Correct: wdk.NewEditor(true, false) // (singleLine, enableSubmit)
5. IconWidget is a Function
Wrong: icon.Layout(gtx)
Correct: icon(gtx, color)
Signature: type IconWidget func(gtx layout.Context, foreground theme.MatColor) layout.Dimensions
6. Chip Layout Arguments
Wrong: chip.Layout(gtx)
Correct: chip.Layout(gtx, "Label")
7. Theme Color Access
Wrong: materialTheme.Scheme.OnBackground.Color
Correct: materialTheme.Scheme.Background.OnColor
MatColorSet structure: {Color, OnColor}
8. Missing Imports
Common required imports:
import (
"image"
"sync"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"github.com/ddkwork/ux/app"
)
Debugging Checklist
- โ
Use factory functions for widgets
- โ
Check nil pointers before Layout calls
- โ
Verify method signatures match
- โ
Ensure all required imports present
- โ
Check theme color access pattern
- โ
Use
wdk.GetMaterialTheme(gtx) for theme access
- โ
Use
treetable.TreeTable for large datasets
- โ
Process table updates in Update() method, not background thread
- โ
Call
app.RequestRedraw() after background work
- โ
Use
sync.Mutex for thread-safe data sharing