diff --git a/doc/development_plan.md b/doc/development_plan.md index 063789e..5ca6dd9 100644 --- a/doc/development_plan.md +++ b/doc/development_plan.md @@ -934,3 +934,15 @@ grabbable from the uncovered top strip of their boxes. Regressions: `internal/editor/selection_menu_track_test.go` (tracking + above-placement arithmetic + first-line flip). + +## 16. Top bar: single row (2026-08-18) + +The dead cut/copy/paste icon row was removed from the editor top bar +(clipboard actions live exclusively in the floating selection menu, the +native pattern). The bar is now one 32dp row — back icon + filename — +down from two rows at 52dp, giving the editor 20dp more height. The +menu's "above first" placement (section 15) is unchanged; with the +shorter bar, selections on the first lines flip below because there is +no room above, and the menu may overlap the top bar when clamped high — +both are the native toolbar's behaviour (transient, dismissed by +tapping elsewhere). diff --git a/internal/editor/filename_test.go b/internal/editor/filename_test.go index 16b951c..5bbc67e 100644 --- a/internal/editor/filename_test.go +++ b/internal/editor/filename_test.go @@ -21,9 +21,14 @@ func TestEditorLayout_Filename(t *testing.T) { // Find top bar (assuming it's the first container element) topBar := elements[0].(ui.Container) - // Find filename label (assuming it's the first label in the top bar) - filenameLabel := topBar.Children[0].(ui.Label) - + // Find the filename label in the top bar (the back icon precedes it). + var filenameLabel ui.Label + for _, c := range topBar.Children { + if l, ok := c.(ui.Label); ok { + filenameLabel = l + break + } + } if filenameLabel.Text != expectedFilename { t.Errorf("expected filename %q, got %q", expectedFilename, filenameLabel.Text) } @@ -44,9 +49,14 @@ func TestEditorLayout_DefaultFilename(t *testing.T) { // Find top bar (assuming it's the first container element) topBar := elements[0].(ui.Container) - // Find filename label (assuming it's the first label in the top bar) - filenameLabel := topBar.Children[0].(ui.Label) - + // Find the filename label in the top bar (the back icon precedes it). + var filenameLabel ui.Label + for _, c := range topBar.Children { + if l, ok := c.(ui.Label); ok { + filenameLabel = l + break + } + } expectedDefault := "untitled.txt" if filenameLabel.Text != expectedDefault { t.Errorf("expected default filename %q, got %q", expectedDefault, filenameLabel.Text) diff --git a/internal/editor/selection_menu_track_test.go b/internal/editor/selection_menu_track_test.go index d119c21..dae8ec5 100644 --- a/internal/editor/selection_menu_track_test.go +++ b/internal/editor/selection_menu_track_test.go @@ -70,8 +70,8 @@ func TestSelectionMenu_FollowsTextAcrossScroll(t *testing.T) { } y0 := e.MenuRect.Y // The menu sits ABOVE the anchor's line: lineTop(line 5) = reg.Y + 5*lh - // (reg.Y = 10+52, the anchor's visual line index is 5). - wantY0 := ui.Dp(62) + ui.Dp(5*lh) - menuH - 8 + // (reg.Y = 10+32, the anchor's visual line index is 5). + wantY0 := ui.Dp(42) + ui.Dp(5*lh) - menuH - 8 if !dpeq(y0, wantY0) { t.Fatalf("initial menu Y = %v, want %v (above the anchor line)", y0, wantY0) } @@ -150,9 +150,9 @@ func TestSelectionMenu_FlipsBelowOnFirstLine(t *testing.T) { if !e.MenuVisible { t.Fatal("menu not shown") } - // Line 0 top = reg.Y = 62; above would be 62-52-8 = 2 < 8, so the menu - // flips below: 62 + lh + 8. - wantY := ui.Dp(62) + EffectiveLineHeight() + 8 + // Line 0 top = reg.Y = 42; above would be 42-52-8 = -18 < 8, so the menu + // flips below: 42 + lh + 8. + wantY := ui.Dp(42) + EffectiveLineHeight() + 8 if !dpeq(e.MenuRect.Y, wantY) { t.Fatalf("menu Y = %v, want %v (flipped below the first line)", e.MenuRect.Y, wantY) } diff --git a/internal/editor/state.go b/internal/editor/state.go index 8054fc8..c4a8435 100644 --- a/internal/editor/state.go +++ b/internal/editor/state.go @@ -1601,11 +1601,13 @@ func markDirty() { func EditorLayout(screenWidth, screenHeight ui.Dp, wordWrap bool) []ui.Element { margin := ui.Dp(10) - // --- Top bar: filename on row 1, icons on row 2 --- + // --- Top bar: one row, back icon + filename. Cut/copy/paste live in the + // floating selection menu (the native Android pattern); the dead icon row + // is gone, saving 20dp of editor height. --- statusBarRegion := ui.Region{ X: margin, Y: margin, W: screenWidth - margin*2, - H: ui.Dp(52), + H: ui.Dp(32), } statusBarW := statusBarRegion.W filename := TheState.Editor.Filename @@ -1616,14 +1618,9 @@ func EditorLayout(screenWidth, screenHeight ui.Dp, wordWrap bool) []ui.Element { statusBarRegion, ui.Color{R: 230, G: 230, B: 230, A: 255}, []ui.Element{ - // Row 1: filename - ui.NewLabel(filename, 14, ui.Region{X: 0, Y: ui.Dp(2), W: statusBarW, H: ui.Dp(20)}, ui.AlignStart, "", nil), - // Row 2: back, cut, copy, paste icons - ui.NewIcon("back", ui.Region{X: ui.Dp(0), Y: ui.Dp(28), W: ui.IconSize, H: ui.IconSize}, 0, + ui.NewIcon("back", ui.Region{X: ui.Dp(0), Y: ui.Dp(4), W: ui.IconSize, H: ui.IconSize}, 0, []ui.Interaction{{Gesture: ui.Tap, Handler: GoToBrowser}}), - ui.NewIcon("cut", ui.Region{X: ui.Dp(48), Y: ui.Dp(28), W: ui.IconSize, H: ui.IconSize}, 0, nil), - ui.NewIcon("copy", ui.Region{X: ui.Dp(96), Y: ui.Dp(28), W: ui.IconSize, H: ui.IconSize}, 0, nil), - ui.NewIcon("paste", ui.Region{X: ui.Dp(144), Y: ui.Dp(28), W: ui.IconSize, H: ui.IconSize}, 0, nil), + ui.NewLabel(filename, 14, ui.Region{X: ui.Dp(32), Y: ui.Dp(6), W: statusBarW - ui.Dp(32), H: ui.Dp(20)}, ui.AlignStart, "", nil), }, ) diff --git a/internal/test/e2e/scroll_cursor_test.go b/internal/test/e2e/scroll_cursor_test.go index 7435fc9..75e4471 100644 --- a/internal/test/e2e/scroll_cursor_test.go +++ b/internal/test/e2e/scroll_cursor_test.go @@ -85,13 +85,13 @@ func TestEditorClickToMoveCursorWithScroll(t *testing.T) { Handler: func(data any) { if pt, ok := data.(ui.Point); ok { // Manually simulate the offset correction that EditorLayout does - // editorRegion.Y is margin(10) + statusBarH(52) = 62. - // Let's set pt.Y to 10 + 62 = 72. - localY := float64(pt.Y) - 62.0 + float64(editor.TheState.ScrollOffset) + // editorRegion.Y is margin(10) + statusBarH(32) = 42. + // Let's set pt.Y to 10 + 42 = 52. + localY := float64(pt.Y) - 42.0 + float64(editor.TheState.ScrollOffset) editor.SetCursorFromPoint(float64(pt.X), localY) } }, - Data: ui.Point{X: 10, Y: 72}, + Data: ui.Point{X: 10, Y: 52}, }, })