package codeview import ( "image" "testing" "time" "gioui.org/f32" "gioui.org/io/input" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/layout" "gioui.org/op" "gioui.org/text" "gioui.org/unit" "gioui.org/widget/material" "loov.dev/lensm/internal/disasm" "loov.dev/lensm/internal/gui" "loov.dev/lensm/internal/syntax" ) func TestCodeUIStyleLayoutWithHelpAndSelection(t *testing.T) { base := material.NewTheme() base.Shaper = text.NewShaper(text.WithCollection(gui.LoadFonts(""))) base.TextSize = unit.Sp(12) theme := gui.NewTheme(base, false) state := &UI{Code: &disasm.Code{ Name: "main.example", File: "main.go", MaxJump: 1, Insts: []disasm.Inst{ {Text: "MOV (R2), R1", NativeText: "mov (%r2), %r1"}, {Text: "ADDQ $1, R1", NativeText: "addq $1, %r1"}, }, Source: []disasm.Source{{ File: "main.go", Blocks: []disasm.SourceBlock{{ LineRange: disasm.LineRange{From: 1, To: 2}, Lines: []string{"func example() {", "}"}, }}, }}, }} state.mousePosition = f32.Pt(100, 22) var router input.Router var operations op.Ops newContext := func() layout.Context { operations.Reset() return layout.Context{ Ops: &operations, Source: router.Source(), Metric: unit.Metric{PxPerDp: 1, PxPerSp: 1}, Now: time.Now(), Constraints: layout.Exact(image.Pt(1200, 600)), } } copied := "" style := Style{ UI: state, Theme: theme, Syntax: syntax.PaletteFor(syntax.StyleGoLand, theme.Colors.SyntaxColors()), ShowNative: true, TextHeight: theme.TextSize, CopyText: func(_ layout.Context, text string) { copied = text }, } gtx := newContext() if got := style.Layout(gtx).Size; got != gtx.Constraints.Max { t.Fatalf("Layout size = %v, want %v", got, gtx.Constraints.Max) } router.Frame(gtx.Ops) router.Queue( pointer.Event{Source: pointer.Mouse, PointerID: 1, Buttons: pointer.ButtonPrimary, Kind: pointer.Press, Position: f32.Pt(100, 20)}, pointer.Event{Source: pointer.Mouse, PointerID: 1, Buttons: pointer.ButtonPrimary, Kind: pointer.Move, Position: f32.Pt(100, 40)}, pointer.Event{Source: pointer.Mouse, PointerID: 1, Buttons: pointer.ButtonPrimary, Kind: pointer.Release, Position: f32.Pt(100, 40)}, ) gtx = newContext() style.Layout(gtx) router.Frame(gtx.Ops) if from, to, ok := state.Selection.Range(); !ok || from != 0 || to != 1 || state.Selection.View != ViewGoAsm { t.Fatalf("drag selection = %#v, range %d..%d", state.Selection, from, to) } if !gtx.Focused(state) { t.Fatal("code view did not retain keyboard focus after drag selection") } router.Queue(key.Event{Name: key.Name("C"), Modifiers: key.ModShortcut, State: key.Press}) gtx = newContext() style.Layout(gtx) router.Frame(gtx.Ops) if want := "MOV (R2), R1\nADDQ $1, R1\n"; copied != want { t.Fatalf("copied text = %q, want %q", copied, want) } // A fast drag past the bottom edge must clamp to the last line, not // stop at the last in-range drag sample. router.Queue( pointer.Event{Source: pointer.Mouse, PointerID: 2, Buttons: pointer.ButtonPrimary, Kind: pointer.Press, Position: f32.Pt(100, 20)}, pointer.Event{Source: pointer.Mouse, PointerID: 2, Buttons: pointer.ButtonPrimary, Kind: pointer.Move, Position: f32.Pt(100, 5000)}, pointer.Event{Source: pointer.Mouse, PointerID: 2, Buttons: pointer.ButtonPrimary, Kind: pointer.Release, Position: f32.Pt(100, 5000)}, ) gtx = newContext() style.Layout(gtx) router.Frame(gtx.Ops) if from, to, ok := state.Selection.Range(); !ok || from != 0 || to != len(state.Code.Insts)-1 { t.Fatalf("overshoot drag selection = %#v, range %d..%d", state.Selection, from, to) } }