diff --git a/doc/element_model.md b/doc/element_model.md index ff1d2de..2e7db59 100644 --- a/doc/element_model.md +++ b/doc/element_model.md @@ -215,26 +215,25 @@ type StatusBar struct { /* region, visible, id — unexported */ Filename string // e.g., "notes.txt" (truncated with ellipsis if too long) FilenameExp bool // true = show full filename (multi-line), false = truncated - Right string // e.g., "1024 / 50000 bytes" CutCopy bool // true = show cut icon in left slot Copy bool // true = show copy icon in left slot (next to cut) - Search bool // true = show search icon (toggle search bar) - Paste bool // true = show paste icon in right slot - ConflictIcon bool // true = show conflict warning icon (rightmost) + Paste bool // true = show paste icon in left slot (next to copy) + ConflictIcon bool // true = show conflict warning icon (right side) + Search bool // true = show search icon (right side, next to conflict) } ``` The status bar appears at the top of the editor page. It has a **two-line layout**: - **Line 1**: Filename (truncated with ellipsis if too long). Tapping the ellipsis toggles between truncated and full multi-line view. -- **Line 2**: Action icons (Cut, Copy, Search, Paste) + file size + conflict icon. +- **Line 2**: Action icons (Cut, Copy, Paste) on the left + conflict icon + search icon on the right. **Fixed icon slots** (24×24 DP each, fixed positions): - **Cut icon**: leftmost action icon - **Copy icon**: second from left (36 DP from Cut icon) -- **Search icon**: third from left (36 DP from Copy icon) -- **Paste icon**: fourth from left (36 DP from Search icon) -- **Conflict icon**: rightmost +- **Paste icon**: third from left (36 DP from Copy icon) +- **Conflict icon**: right side (36 DP from Search icon) +- **Search icon**: rightmost (36 DP from Conflict icon) Icons appear/disappear without reflowing other elements. The StatusBar's `Region.H` is computed dynamically based on whether the filename is truncated (2 lines) or expanded (3+ lines). @@ -246,21 +245,23 @@ When a sync conflict is detected for the active file, `ConflictIcon` is set to t type BottomBar struct { /* region, visible, id — unexported */ CursorPos string // e.g., "Ln 10, Col 5" - WordWrap bool // true = word wrap is enabled (shown for reference) + BytePos string // e.g., "1024 / 50000 bytes" + WordWrap bool // true = word wrap is enabled (button: tap to toggle) } ``` -The bottom bar appears at the bottom of the editor page. It displays cursor position and editor state information. It is always visible (no icons, just text). +The bottom bar appears at the bottom of the editor page. It displays cursor position, byte position, and word wrap status. It is always visible. **Layout**: ``` ┌──────────────────────────────────────────────────────────────┐ -│ Ln 10, Col 5 Word Wrap: On │ +│ Ln 10, Col 5 1024 / 50000 [Word Wrap: On] │ └──────────────────────────────────────────────────────────────┘ ``` - **Left**: Cursor position (line number, column number) -- **Right**: Word wrap status (On/Off) +- **Center**: Byte position (current byte / total file bytes) +- **Right**: Word wrap button (tap to toggle On/Off) The BottomBar's `Region.H` is fixed (24 DP). It sits below the editor content. @@ -381,13 +382,12 @@ Elements that don't specify explicit colors/sizes use theme defaults. The theme []Element{ StatusBar{ Filename: "notes.txt", FilenameExp: false, - Right: "1024 / 50000", - CutCopy: true, Copy: false, Search: false, Paste: true, ConflictIcon: false, + CutCopy: true, Copy: false, Paste: true, ConflictIcon: false, Search: false, }, SearchBar{Query: "foo", Match: 1, Total: 3}, // search (when active) TextField{Multiline: true, VisibleLines: [...], ScrollOffset: 420, WordWrap: true}, Cursor{Line: 5, Column: 12, Selection: &Selection{...}}, - BottomBar{CursorPos: "Ln 47, Col 12", WordWrap: true}, + BottomBar{CursorPos: "Ln 47, Col 12", BytePos: "1024 / 50000", WordWrap: true}, } ``` @@ -395,12 +395,13 @@ The search bar is only present when the user has activated search. When visible, The StatusBar contains: - **Filename** on line 1 (truncated with ellipsis if too long) -- **Action icons** (Cut, Copy, Search, Paste) on line 2, fixed positions -- **File size** between Paste and Conflict icons +- **Action icons** (Cut, Copy, Paste) on line 2, left side +- **Conflict icon** and **Search icon** on line 2, right side The BottomBar contains: - **Cursor position** on the left (e.g., "Ln 47, Col 12") -- **Word wrap status** on the right (e.g., "Word Wrap: On") +- **Byte position** in the center (e.g., "1024 / 50000") +- **Word wrap button** on the right (tap to toggle On/Off) ### 8.3 Merge Page diff --git a/doc/touch.md b/doc/touch.md index 66408ca..3d30962 100644 --- a/doc/touch.md +++ b/doc/touch.md @@ -303,25 +303,24 @@ The StatusBar (top bar) has **fixed icon slots** for cut, copy, and paste. Icons ``` ┌──────────────────────────────────────────────────────────────┐ │ filename.txt │ ← Line 1: filename (truncated with ellipsis) -│ [Cut] [Copy] [Search] [Paste] 1024 / 50000 [Conf] │ ← Line 2: action icons + file size +│ [Cut] [Copy] [Paste] [Conf] [Search] │ ← Line 2: action icons (left/right) └──────────────────────────────────────────────────────────────┘ StatusBar (top bar) ┌──────────────────────────────────────────────────────────────┐ │ TextField (editor content) │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ -│ Ln 10, Col 5 Word Wrap: On │ ← BottomBar +│ Ln 10, Col 5 1024 / 50000 [Word Wrap: On] │ ← BottomBar └──────────────────────────────────────────────────────────────┘ ``` **Fixed slot positions** (in Dp, from the edges): - **Cut icon**: leftmost action icon (24×24 DP, fixed position) - **Copy icon**: second from left (24×24 DP, fixed position, 36 DP from Cut icon) -- **Search icon**: third from left (24×24 DP, fixed position, 36 DP from Copy icon) -- **Paste icon**: fourth from left (24×24 DP, fixed position, 36 DP from Search icon) -- **Conflict icon**: rightmost (24×24 DP, fixed position) +- **Paste icon**: third from left (24×24 DP, fixed position, 36 DP from Copy icon) +- **Conflict icon**: right side (24×24 DP, fixed position, 36 DP from Search icon) +- **Search icon**: rightmost (24×24 DP, fixed position) - **Filename**: top line, truncated with ellipsis if too long -- **File size**: between Paste and Conflict icons **Visibility rules**: @@ -329,9 +328,9 @@ The StatusBar (top bar) has **fixed icon slots** for cut, copy, and paste. Icons |---|---| | **Cut** | `selection_start != selection_end` | | **Copy** | `selection_start != selection_end` | -| **Search** | Always visible (toggle search bar) | | **Paste** | `clipboard != ""` | | **Conflict** | sync conflict detected for active file | +| **Search** | Always visible (toggle search bar) | Icons are rendered as `Button` elements with fixed sizes. The StatusBar's `Region` height is computed dynamically based on whether the filename line is visible (2 lines when filename is shown, 1 line when in merge/search mode). @@ -433,31 +432,32 @@ This field is **not** persisted to disk — it is reset on app restart (same as In the truncated view, the StatusBar has 2 lines: ``` Line 1: filename.txt... -Line 2: [Cut] [Copy] [Search] [Paste] 1024 / 50000 [Conf] +Line 2: [Cut] [Copy] [Paste] [Conf] [Search] ``` In the full view, the StatusBar has 3+ lines (depending on filename length): ``` Line 1: very_long_filename_that_does_not_fit_on_a_single_line.txt -Line 2: [Cut] [Copy] [Search] [Paste] 1024 / 50000 [Conf] +Line 2: [Cut] [Copy] [Paste] [Conf] [Search] ``` The `Region.H` of the StatusBar is computed dynamically based on the number of lines. The BottomBar is always at the bottom of the screen (24 DP fixed height). ## 5.8 Bottom Bar -The Bottom Bar appears at the bottom of the editor page. It displays cursor position and editor state information. It is always visible (no icons, just text). +The Bottom Bar appears at the bottom of the editor page. It displays cursor position, byte position, and word wrap status. It is always visible. ### 5.8.1 Layout ``` ┌──────────────────────────────────────────────────────────────┐ -│ Ln 10, Col 5 Word Wrap: On │ +│ Ln 10, Col 5 1024 / 50000 [Word Wrap: On] │ └──────────────────────────────────────────────────────────────┘ ``` - **Left**: Cursor position (e.g., "Ln 10, Col 5") -- **Right**: Word wrap status (e.g., "Word Wrap: On") +- **Center**: Byte position (e.g., "1024 / 50000") +- **Right**: Word wrap button (tap to toggle On/Off) The BottomBar's `Region.H` is fixed at 24 DP. It sits below the editor content. @@ -468,13 +468,28 @@ The cursor position is computed from the current byte offset: 2. The `CursorPos` field is set to `"Ln {line}, Col {column}"`. 3. The field is updated on every cursor movement (tap, arrow keys, backspace, etc.). -### 5.8.3 Word Wrap Status +### 5.8.3 Byte Position -The word wrap status is a simple boolean field (`word_wrap_enabled`): +The byte position shows the current byte offset and total file size: +- **Format**: `"current / total"` (e.g., "1024 / 50000") +- **Current**: byte offset of the cursor in the file +- **Total**: total file size in bytes +- The field is updated on every cursor movement. + +### 5.8.4 Word Wrap Button + +The word wrap button toggles word wrap on/off: - `true` → "Word Wrap: On" - `false` → "Word Wrap: Off" -This field is persisted to `.pad/state.json` and restored on app start. Toggling word wrap is done via a settings screen (not yet implemented). +**Interaction**: +1. User taps the word wrap button in the BottomBar. +2. Main goroutine receives `pointer.Event` (Type: `Press`) with the button's tag. +3. Main goroutine sends `InputEvent{ElementID: "word_wrap"}` to Logic. +4. Logic toggles the `word_wrap_enabled` state field. +5. Logic produces a new frame (BottomBar shows updated status). + +The word wrap status is persisted to `.pad/state.json` and restored on app start. ## 6. Scroll Momentum