| | | 1 | | using System.Text; |
| | | 2 | | |
| | | 3 | | namespace Punch.CLI; |
| | | 4 | | |
| | | 5 | | // Holds the mutable editor state for a single day's TUI session: the schedule |
| | | 6 | | // being edited, the input fields, the cursor/selection, and the view toggles. |
| | | 7 | | // The event loop mutates these in place and the renderer reads them. |
| | | 8 | | internal sealed class PunchSession |
| | | 9 | | { |
| | 84 | 10 | | public PunchSession(DaySchedule schedule, DateOnly workingDate, string filePath, int cursorSlot, int targetHours = 8 |
| | 84 | 11 | | { |
| | 84 | 12 | | Schedule = schedule; |
| | 84 | 13 | | WorkingDate = workingDate; |
| | 84 | 14 | | FilePath = filePath; |
| | 84 | 15 | | CursorSlot = cursorSlot; |
| | 84 | 16 | | TargetHours = targetHours; |
| | 84 | 17 | | } |
| | | 18 | | |
| | 347 | 19 | | public DaySchedule Schedule { get; } |
| | 5 | 20 | | public DateOnly WorkingDate { get; } |
| | 0 | 21 | | public string FilePath { get; } |
| | | 22 | | |
| | | 23 | | // The daily workday goal in whole hours, used for the status-bar percentage. |
| | 2 | 24 | | public int TargetHours { get; } |
| | | 25 | | |
| | 14 | 26 | | public IReadOnlyList<TimeBlock> Blocks => Schedule.Blocks; |
| | | 27 | | |
| | | 28 | | // Input fields. ActiveField: 0 = Description, 1 = Ticket. |
| | 313 | 29 | | public StringBuilder InputBuffer { get; } = new(); |
| | 271 | 30 | | public int InputCursor { get; set; } |
| | 192 | 31 | | public StringBuilder TicketBuffer { get; } = new(); |
| | 104 | 32 | | public int TicketCursor { get; set; } |
| | 505 | 33 | | public int ActiveField { get; set; } |
| | | 34 | | |
| | | 35 | | // Cursor / selection on the timeline. |
| | 252 | 36 | | public int CursorSlot { get; set; } |
| | 205 | 37 | | public int SelectionLength { get; set; } = 1; // number of 15-min slots (min 1) |
| | 456 | 38 | | public TimeBlock? SelectedBlock { get; set; } |
| | 178 | 39 | | public bool Editing { get; set; } |
| | | 40 | | |
| | | 41 | | // View toggles. |
| | 224 | 42 | | public bool ShowHelp { get; set; } |
| | 203 | 43 | | public bool ShowTicketSummary { get; set; } |
| | 509 | 44 | | public int LogScrollOffset { get; set; } |
| | | 45 | | |
| | | 46 | | // Ticket picker overlay. Tickets is reloaded from disk each time the picker |
| | | 47 | | // is opened, so edits to ~/.punch/tickets.txt are picked up without a restart. |
| | 238 | 48 | | public bool ShowTicketPicker { get; set; } |
| | 25 | 49 | | public int TicketPickerCursor { get; set; } |
| | 103 | 50 | | public List<TicketEntry> Tickets { get; set; } = new(); |
| | | 51 | | |
| | | 52 | | // Text input is editable when composing a new entry (no block selected) or |
| | | 53 | | // editing an existing one. |
| | 85 | 54 | | public bool IsInputActive => SelectedBlock == null || Editing; |
| | | 55 | | |
| | | 56 | | // The buffer/cursor for whichever field currently has focus. |
| | 132 | 57 | | public StringBuilder ActiveBuffer => ActiveField == 0 ? InputBuffer : TicketBuffer; |
| | | 58 | | |
| | | 59 | | public int ActiveCursor |
| | | 60 | | { |
| | 152 | 61 | | get => ActiveField == 0 ? InputCursor : TicketCursor; |
| | | 62 | | set |
| | 80 | 63 | | { |
| | 145 | 64 | | if (ActiveField == 0) InputCursor = value; |
| | 15 | 65 | | else TicketCursor = value; |
| | 80 | 66 | | } |
| | | 67 | | } |
| | | 68 | | |
| | | 69 | | // Clears both input fields and returns focus to the Description field, |
| | | 70 | | // leaving edit mode. Does not touch the cursor selection on the timeline. |
| | | 71 | | public void ResetInput() |
| | 53 | 72 | | { |
| | 53 | 73 | | Editing = false; |
| | 53 | 74 | | ActiveField = 0; |
| | 53 | 75 | | InputBuffer.Clear(); |
| | 53 | 76 | | InputCursor = 0; |
| | 53 | 77 | | TicketBuffer.Clear(); |
| | 53 | 78 | | TicketCursor = 0; |
| | 53 | 79 | | } |
| | | 80 | | } |