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
12 changes: 12 additions & 0 deletions builder/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ func (pm *PropertyMapper) applyTextViewProperties(tv *tview.TextView, prim *conf
}
})
}
if prim.Regions && prim.OnHighlighted != "" && pm.executor != nil {
onHighlightedExpr := prim.OnHighlighted
tv.SetHighlightedFunc(func(added, removed, remaining []string) {
if len(added) == 0 {
return
}
pm.context.SetStateDirect("__highlightedRegion", added[0])
if cb, err := pm.executor.ExecuteCallback(onHighlightedExpr); err == nil {
cb()
}
})
}
return nil
}

Expand Down
74 changes: 74 additions & 0 deletions builder/properties_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package builder

import (
"testing"

"github.com/cassdeckard/tviewyaml/config"
"github.com/cassdeckard/tviewyaml/template"
"github.com/rivo/tview"
)

func TestApplyTextViewProperties_OnHighlighted(t *testing.T) {
app := tview.NewApplication()
pages := tview.NewPages()
ctx := template.NewContext(app, pages)
registry := template.NewFunctionRegistry()
executor := template.NewExecutor(ctx, registry)
pm := NewPropertyMapper(ctx, executor)

tv := tview.NewTextView()
tv.SetRegions(true)
tv.SetText(`["a"]Region A[""]`)

prim := &config.Primitive{
Type: "textView",
Regions: true,
OnHighlighted: `{{ showNotification "highlighted" }}`,
}

err := pm.ApplyProperties(tv, prim)
if err != nil {
t.Fatalf("ApplyProperties: %v", err)
}
// Wiring succeeded; SetHighlightedFunc is attached. The callback runs when
// tview draws after a highlight change (tested via acceptance if needed).
}

func TestBuildFlex_TextViewWithOnHighlighted(t *testing.T) {
app := tview.NewApplication()
pages := tview.NewPages()
ctx := template.NewContext(app, pages)
registry := template.NewFunctionRegistry()
b := NewBuilder(ctx, registry)

pageConfig := &config.PageConfig{
Type: "flex",
Items: []config.FlexItem{
{
Primitive: &config.Primitive{
Type: "textView",
Regions: true,
DynamicColors: true,
Text: `["slide1"]Slide 1[""] ["slide2"]Slide 2[""]`,
OnHighlighted: `{{ switchToPage "main" }}`,
},
FixedSize: 0,
Proportion: 1,
Focus: true,
},
},
}

result, err := b.BuildFromConfig(pageConfig)
if err != nil {
t.Fatalf("BuildFromConfig: %v", err)
}

flex, ok := result.(*tview.Flex)
if !ok {
t.Fatalf("expected *tview.Flex, got %T", result)
}
if flex.GetItemCount() != 1 {
t.Errorf("GetItemCount() = %d, want 1", flex.GetItemCount())
}
}
2 changes: 2 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ type Primitive struct {
OnCancel string `yaml:"onCancel,omitempty"` // Template expression when form is cancelled (Escape); if unset and OnSubmit set, Escape runs OnSubmit
// onDone: Template expression when user presses Enter/Escape (TextView, InputField, Table)
OnDone string `yaml:"onDone,omitempty"`
// onHighlighted: Template expression when highlighted region changes (TextView with regions; state: __highlightedRegion)
OnHighlighted string `yaml:"onHighlighted,omitempty"`
// Table-specific properties
OnCellSelected string `yaml:"onCellSelected,omitempty"` // Template expression when a cell is selected (state: __selectedCellText, __selectedRow, __selectedCol)
Borders bool `yaml:"borders,omitempty"` // Show borders between cells
Expand Down
6 changes: 5 additions & 1 deletion docs/tview-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This table maps [tview](https://pkg.go.dev/github.com/rivo/tview) primitives and
| **Pages** | Yes | Yes (root) | Yes | [app.yaml](../example/config/app.yaml), [nested-pages.yaml](../example/config/nested-pages.yaml) | Tab-like page switching; `ref` to YAML files |
| **Table** | Yes | Yes | Yes | [table.yaml](../example/config/table.yaml) | Headers, rows, borders, fixed rows/columns; `onDone` for Enter/Escape |
| **TextArea** | Yes | No | Yes (Form item) | [form.yaml](../example/config/form.yaml) | Form item type `textarea`; multi-line input |
| **TextView** | Yes | No | Yes | [textview.yaml](../example/config/textview.yaml) | Dynamic colors, regions, scrollable; `onDone` for Enter/Escape |
| **TextView** | Yes | No | Yes | [textview.yaml](../example/config/textview.yaml) | Dynamic colors, regions, scrollable; `onDone` for Enter/Escape; `onHighlighted` for region clicks |
| **TreeView** | Yes | Yes | Yes | [treeview.yaml](../example/config/treeview.yaml), [treeview-standalone.yaml](../example/config/treeview-standalone.yaml), [treeview-modes.yaml](../example/config/treeview-modes.yaml) | Nodes with children; selectable modes |

## Form Item Types
Expand Down Expand Up @@ -48,6 +48,10 @@ TextView, InputField (standalone), and Table support `onDone`—a template expre

Note: tview's Table does not call SetDoneFunc for Enter when rows are selectable—Enter is used for selection. Escape still triggers onDone on selectable tables.

## Callback Support: onHighlighted (TextView)

TextView with `regions: true` supports `onHighlighted`—a template expression that runs when the highlighted region changes (from clicks or Tab/Enter navigation). State `__highlightedRegion` is set to the first newly highlighted region ID before the callback runs. Use `switchToPage "{{ bindState __highlightedRegion }}"` for presentation-style info bar navigation. See [textview.yaml](../example/config/textview.yaml).

## Additional Example Configs (Feature Demos)

| Config | Purpose |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
 Enter to start selection, Tab/Backtab to navigate regions, arrow keys to scroll, ESC to return

 Status: 

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
 Text display with dynamic colors, 
 regions, highlights, and scrolling
╔═══TextView with Colors and Regions═══╗
║TextView Features: ║
║ ║
╚══════════════════════════════════════╝
 Enter to start selection, Tab/Backtab 
 to navigate regions, arrow keys to 
 to navigate regions, arrow keys to 
 scroll, ESC to return

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
║ ║
║4. Word Wrap: Long lines automatically wrap to fit the view ║
║ ║
║Press Enter to start region selection, Tab/Backtab to navigate regions ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝
 Enter to start selection, Tab/Backtab to navigate regions, arrow keys to 
 scroll, ESC to return
 scroll, ESC to return

 Status: 
9 changes: 7 additions & 2 deletions example/config/textview.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ items:
title: "TextView with Colors and Regions"
dynamicColors: true
regions: true
onHighlighted: '{{ showHighlightedNotification }}'
text: |
[yellow]TextView Features:[white]

Expand All @@ -40,7 +41,11 @@ items:
type: textView
textAlign: center
textColor: gray
text: "Enter to start selection, Tab/Backtab to navigate regions, arrow keys to scroll, ESC to return"
fixedSize: 2
dynamicColors: true
text: |
Enter to start selection, Tab/Backtab to navigate regions, arrow keys to scroll, ESC to return

[green]Status:[white] {{ bindState notification }}
fixedSize: 4
proportion: 1
focus: false
11 changes: 11 additions & 0 deletions template/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ func registerBuiltinFunctions(registry *FunctionRegistry) {
ctx.SetStateDirect("notification", msg)
})

// showHighlightedNotification: sets notification to "Region X selected" using __highlightedRegion.
// For use in onHighlighted callbacks to show which region was selected.
registry.Register("showHighlightedNotification", 0, intPtr(0), nil, func(ctx *Context) {
region, ok := ctx.GetState("__highlightedRegion")
msg := "Region selected"
if ok && fmt.Sprint(region) != "" {
msg = fmt.Sprintf("Region %s selected", region)
}
ctx.SetStateDirect("notification", msg)
})

// switchToPage: switches to a different page
registry.Register("switchToPage", 1, intPtr(1), nil, func(ctx *Context, pageName string) {
if ctx.Pages != nil {
Expand Down
Loading