Collapse editor top bar to one row (back + filename)
The cut/copy/paste icons were dead placeholders (no handlers); clipboard actions live in the floating selection menu, so the second icon row is removed. The bar goes from two rows at 52dp to one row at 32dp, giving the editor 20dp more height. - filename_test.go now finds the label by type (back icon precedes it) - menu tracking tests updated for the new editorY (reg.Y = 10+32) - scroll_cursor e2e simulation updated to match - On-device verified: back button works, floating menu places above with room and flips below on the first lines, handles unaffected.
This commit is contained in:
parent
dc35aa2e36
commit
2b7c9cd3eb
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user