- commit
- 51ba889c080dbea07cf5d909c921312bc1301f60
- parent
- 3c438191f90bc3a5c8901e9e2c5dddf7bdd9b248
- author
- Dami <dami.etoile@gmail.com>
| M | app/ | 4 | +- |
| A | app/ | 76 | ++++++++++++++++++++++++++++++ |
| M | shikaku/ | 11 | ++++- |
| M | shikaku/ | 47 | +++++++++++++++++++ |
@@ -98,7 +98,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9898 return m.handleEnter()9999 }100100 case key.Matches(msg, rootKeys.Escape):101- return m.handleEscape()101+ if m.state != gameView {102+ return m.handleEscape()103+ }102104 case key.Matches(msg, rootKeys.Quit):103105 m = saveCurrentGame(m, store.StatusAbandoned)104106 return m, tea.Quit@@ -0,0 +1,76 @@
1+package app2+3+import (4+ "testing"5+6+ "charm.land/bubbles/v2/key"7+ tea "charm.land/bubbletea/v2"8+ "github.com/FelineStateMachine/puzzletea/game"9+)10+11+type escapeTrackingGame struct {12+ updateCalls int13+ sawEscape bool14+}15+16+func (g *escapeTrackingGame) GetDebugInfo() string {17+ return ""18+}19+20+func (g *escapeTrackingGame) GetFullHelp() [][]key.Binding {21+ return nil22+}23+24+func (g *escapeTrackingGame) GetSave() ([]byte, error) {25+ return nil, nil26+}27+28+func (g *escapeTrackingGame) IsSolved() bool {29+ return false30+}31+32+func (g *escapeTrackingGame) Reset() game.Gamer {33+ return g34+}35+36+func (g *escapeTrackingGame) SetTitle(string) game.Gamer {37+ return g38+}39+40+func (g *escapeTrackingGame) Init() tea.Cmd {41+ return nil42+}43+44+func (g *escapeTrackingGame) View() string {45+ return ""46+}47+48+func (g *escapeTrackingGame) Update(msg tea.Msg) (game.Gamer, tea.Cmd) {49+ g.updateCalls++50+ keyMsg, ok := msg.(tea.KeyPressMsg)51+ if ok && keyMsg.Code == tea.KeyEscape {52+ g.sawEscape = true53+ }54+ return g, nil55+}56+57+func TestGameViewEscapePassesThroughToGame(t *testing.T) {58+ tracker := &escapeTrackingGame{}59+ m := model{60+ state: gameView,61+ game: tracker,62+ }63+64+ next, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})65+ got := next.(model)66+67+ if got.state != gameView {68+ t.Fatalf("expected to remain in gameView, got %d", got.state)69+ }70+ if tracker.updateCalls != 1 {71+ t.Fatalf("expected game Update to be called once, got %d", tracker.updateCalls)72+ }73+ if !tracker.sawEscape {74+ t.Fatal("expected game to receive escape key")75+ }76+}@@ -249,6 +249,13 @@ func rectFromCorners(x1, y1, x2, y2 int) Rectangle {
249249 }250250 }251251 252+func (m Model) cancelPendingRectangle() Model {253+ m.selectedClue = nil254+ m.mouseDragAnchor = nil255+ m.mousePreview = nil256+ return m257+}258+252259 func (m Model) handleNavMode(msg tea.KeyPressMsg) Model {253260 switch {254261 case key.Matches(msg, m.keys.Select):@@ -270,6 +277,8 @@ func (m Model) handleNavMode(msg tea.KeyPressMsg) Model {
270277 if owner >= 0 {271278 m.puzzle.RemoveRectangle(owner)272279 }280+ case key.Matches(msg, m.keys.Cancel):281+ m = m.cancelPendingRectangle()273282 default:274283 m.cursor.Move(m.keys.CursorKeyMap, msg, m.puzzle.Width-1, m.puzzle.Height-1)275284 }@@ -325,7 +334,7 @@ func (m Model) handleExpansionMode(msg tea.KeyPressMsg) Model {
325334 m.selectedClue = nil326335 }327336 case key.Matches(msg, m.keys.Cancel):328- m.selectedClue = nil337+ m = m.cancelPendingRectangle()329338 case key.Matches(msg, m.keys.Delete):330339 m.puzzle.RemoveRectangle(clue.ID)331340 m.selectedClue = nil@@ -4,6 +4,8 @@ import (
44 "encoding/json"55 "math/rand/v2"66 "testing"7+8+ tea "charm.land/bubbletea/v2"79 )810 911 // --- helpers ---@@ -579,6 +581,51 @@ func TestRectFromCorners(t *testing.T) {
579581 }580582 }581583 584+// --- Escape cancel behavior (P1) ---585+586+func TestEscapeCancelsPendingExpansion(t *testing.T) {587+ p := simplePuzzle()588+ selected := 0589+ m := Model{590+ puzzle: *p,591+ selectedClue: &selected,592+ expansion: expansion{593+ clueID: 0,594+ right: 1,595+ },596+ keys: DefaultKeyMap,597+ }598+599+ next, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})600+ got := next.(Model)601+602+ if got.selectedClue != nil {603+ t.Fatal("expected selected clue to be cleared on escape")604+ }605+}606+607+func TestEscapeCancelsPendingMousePreviewInNavMode(t *testing.T) {608+ p := simplePuzzle()609+ anchor := [2]int{0, 0}610+ preview := Rectangle{X: 0, Y: 0, W: 2, H: 2}611+ m := Model{612+ puzzle: *p,613+ keys: DefaultKeyMap,614+ mouseDragAnchor: &anchor,615+ mousePreview: &preview,616+ }617+618+ next, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})619+ got := next.(Model)620+621+ if got.mouseDragAnchor != nil {622+ t.Fatal("expected mouse drag anchor to be cleared on escape")623+ }624+ if got.mousePreview != nil {625+ t.Fatal("expected mouse preview to be cleared on escape")626+ }627+}628+582629 // --- CluesInRect (P1) ---583630 584631 func TestCluesInRect(t *testing.T) {