From 9104da1fb031a420dfc8cc2727f9eede9fe790c1 Mon Sep 17 00:00:00 2001 From: No0ne558 Date: Thu, 28 May 2026 10:38:54 -0700 Subject: [PATCH] feat: Index Tab selected feedback, no-op guard, page-zero block, auto-page creation - IndexTabZone::State() returns selected state (1) when the terminal's current page matches the tab's jump_id, giving automatic visual feedback using the tab's configured selected-state edge, texture, and font color. - IndexTabZone::Touch() returns SIGNAL_IGNORED when already on the target page, preventing a redundant jump. - ZoneDB::Add() now rejects page id == 0 with a ReportError. - ZoneDB::NextAvailablePageID(start_id, page_size) added: returns the first unused page ID >= start_id for the given screen size. - Terminal::ReadZone(): when a brand-new ZONE_INDEX_TAB is saved with no jump target (JUMP_NONE), a PAGE_ITEM menu page is automatically created from the button name at the first available ID >= 60, and the button is wired to JUMP_NORMAL pointing at it. --- docs/changelog.md | 9 +++++++++ main/hardware/terminal.cc | 22 ++++++++++++++++++++++ zone/button_zone.cc | 20 ++++++++++++++++++++ zone/button_zone.hh | 6 +++++- zone/zone.cc | 15 +++++++++++++++ zone/zone.hh | 2 ++ 6 files changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 05a6971e..3c70f5e5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +### Added +- **Index Tab Buttons: Selected visual feedback, no-op guard, page-zero block, and auto-page creation** (2026-05-28) + - `IndexTabZone::State()` now returns the selected state (1) whenever the terminal's current page matches the tab's `jump_id`. The tab for the active page is automatically rendered with its configured selected-state edge, texture, and font color (`frame[1]`, `texture[1]`, `color[1]`) — no new `Terminal` fields required. + - `IndexTabZone::Touch()` returns `SIGNAL_IGNORED` when the terminal is already on the tab's target page, preventing a redundant page jump. + - `ZoneDB::Add(Page*)` now rejects any page with `id == 0`, reporting an error. This is enforced at the single authoritative insertion point so no page with number zero can be created through any code path. + - `ZoneDB::NextAvailablePageID(int start_id, int page_size)` helper added — returns the first unused page ID ≥ `start_id` for the given screen size. + - When a **brand-new** `ZONE_INDEX_TAB` button is saved (via `Terminal::ReadZone()`), and it has no jump target yet (`JUMP_NONE`), a new `PAGE_ITEM` menu page is automatically created using the button's name, assigned the first available ID ≥ 60, and the button is set to `JUMP_NORMAL` pointing to that page. Editing an existing Index Tab with an already-configured jump is unaffected. + - Files modified: `zone/button_zone.hh`, `zone/button_zone.cc`, `zone/zone.hh`, `zone/zone.cc`, `main/hardware/terminal.cc`. + ### Work In Progress - **Allow Multiple Terminal Logins (experimental)** (2026-04-12) - Added a persisted soft-switch `allow_multi_login` (bumped `SETTINGS_VERSION` to 107) which allows the same `Employee` to be logged into multiple terminals when enabled. diff --git a/main/hardware/terminal.cc b/main/hardware/terminal.cc index 3d0446cc..ac1f2bf4 100644 --- a/main/hardware/terminal.cc +++ b/main/hardware/terminal.cc @@ -5947,6 +5947,28 @@ int Terminal::ReadZone() edit_zone = nullptr; } + // Auto-create a menu page for a brand-new Index Tab button that has no jump target yet + if (newZone->Type() == ZONE_INDEX_TAB && + edit_zone == nullptr && + newZone->JumpType() && *newZone->JumpType() == JUMP_NONE && + newZone->name.size() > 0) + { + int new_page_id = zone_db->NextAvailablePageID(60, page_size); + Page *autoPage = NewPosPage(); + if (autoPage != nullptr) + { + autoPage->id = new_page_id; + autoPage->size = page_size; + autoPage->type = PAGE_ITEM; + autoPage->index = INDEX_GENERAL; + autoPage->name.Set(newZone->name.Value()); + autoPage->Init(zone_db); + zone_db->Add(autoPage); + *newZone->JumpType() = JUMP_NORMAL; + *newZone->JumpID() = new_page_id; + } + } + if (newZone->Type() == ZONE_COMMENT) currPage->AddFront(newZone); // make sure comment zones are always on top else diff --git a/zone/button_zone.cc b/zone/button_zone.cc index db674008..354bc8bb 100644 --- a/zone/button_zone.cc +++ b/zone/button_zone.cc @@ -1156,6 +1156,26 @@ int IndexTabZone::CanEdit(Terminal *t) return 0; } +int IndexTabZone::State(Terminal *t) +{ + FnTrace("IndexTabZone::State()"); + if (active == 0) + return 2; // disabled + // Show selected appearance when the terminal is currently on this tab's target page + if (jump_id != 0 && t->page != nullptr && t->page->id == jump_id) + return 1; + return stay_lit; // normal (0) or stay-lit +} + +SignalResult IndexTabZone::Touch(Terminal *term, int tx, int ty) +{ + FnTrace("IndexTabZone::Touch()"); + // Already on this tab's target page — nothing to do + if (jump_id != 0 && term->page != nullptr && term->page->id == jump_id) + return SIGNAL_IGNORED; + return ButtonZone::Touch(term, tx, ty); +} + // LanguageButtonZone implementation LanguageButtonZone::LanguageButtonZone() diff --git a/zone/button_zone.hh b/zone/button_zone.hh index 96925dce..06436aea 100644 --- a/zone/button_zone.hh +++ b/zone/button_zone.hh @@ -197,7 +197,11 @@ public: int CanSelect(Terminal *t) override; int CanEdit(Terminal *t) override; std::unique_ptr Copy() override; - int GainFocus(Terminal *term, Zone *oldfocus) override { return 0; } + int GainFocus(Terminal *term, Zone *oldfocus) override { return 0; } + // Returns selected-state (1) when the terminal is currently on this tab's target page + int State(Terminal *t) override; + // No-op when already on this tab's target page; otherwise delegates to ButtonZone + SignalResult Touch(Terminal *term, int tx, int ty) override; }; class LanguageButtonZone : public ButtonZone diff --git a/zone/zone.cc b/zone/zone.cc index f6a03c1b..2087ca22 100644 --- a/zone/zone.cc +++ b/zone/zone.cc @@ -1580,6 +1580,12 @@ int ZoneDB::Add(Page *p) if (p == nullptr) return 1; + if (p->id == 0) + { + ReportError("Page number 0 is not allowed"); + return 1; + } + // start at end of list and work backwords Page *ptr = page_list.Tail(); while (ptr && (p->id < ptr->id || (p->id == ptr->id && p->size > ptr->size))) @@ -1589,6 +1595,15 @@ int ZoneDB::Add(Page *p) return page_list.AddAfterNode(ptr, p); } +int ZoneDB::NextAvailablePageID(int start_id, int page_size) +{ + FnTrace("ZoneDB::NextAvailablePageID()"); + int id = (start_id > 0) ? start_id : 1; + while (FindByID(id, page_size) != nullptr) + ++id; + return id; +} + int ZoneDB::AddUnique(Page *page) { FnTrace("ZoneDB::AddUnique()"); diff --git a/zone/zone.hh b/zone/zone.hh index bbd6b280..e14cd4a9 100644 --- a/zone/zone.hh +++ b/zone/zone.hh @@ -388,6 +388,8 @@ public: int ValidateSystemPages(); // Check for System Pages with invalid parent_id values int PrintZoneDB(const char* dest = nullptr, int brief = 0); // for debugging only + // Returns the first page ID >= start_id that is not already in use for the given size + int NextAvailablePageID(int start_id, int page_size); }; #endif