Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## [Unreleased]

### Fixed
- **Dialog Keyboard: Order Comment Dialog Text Entry Improvements (2026-01-03)** *(Work in Progress)*
- Redesigned Order Comment dialog with modern 6-row QWERTY keyboard layout (previously had overlapping keyboard layers)
- Implemented visible text entry box at top of dialog with absolute positioning
- Fixed text display updates - text now appears in RED as user types on keyboard
- Added proper background clearing to prevent text artifacting when clearing input
- Adjusted text positioning for better vertical centering in entry box
- **Files modified**: `zone/dialog_zone.cc` (OrderCommentDialog constructor, Render, RenderEntry, DrawEntry, Signal methods), `zone/dialog_zone.hh`
- **Status**: Text entry box is visible and functional, keyboard responds to clicks. Still working on final polish and testing.

- **Form Zone: Fix Double-Touch and Click Area Issues in ListField (2026-01-03)**
- Fixed double-triggering of field Touch events when using touchscreen input
- Fixed ListField click detection area not matching the visual button boundaries
- **Root Cause**: Both Touch and Mouse events were calling `FormZone::Touch()` which triggered field actions, causing double-clicks on touchscreens
- **Solution**: Changed `ListFormZone::Touch()` and `ListFormZone::Mouse()` to call `LayoutZone::Touch()` instead, which only updates coordinates without triggering field actions
- **Offset Fix**: Changed ListField offset calculation from `label_width + .6` to `label_width + 1` and boundary from `entry_width + 1` to `entry_width` to match the Render and Mouse methods
- **Files modified**: `zone/form_zone.cc` (`ListFormZone::Touch()`, `ListFormZone::Mouse()`, `ListField::Touch()`)
- **Impact**: Tender Settings and all form zones with list fields now respond correctly to single clicks/taps without double-triggering, and buttons are clickable across their entire visual area

- **CI: Update Linux Builds to C++23 Standard (2026-01-01)**
- Changed CI workflow from C++20 to C++23 to match project requirements
- Restricted CI testing to GCC 14+ which has full C++23 stdlib support
Expand Down
174 changes: 113 additions & 61 deletions zone/dialog_zone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3431,6 +3431,7 @@ OrderCommentDialog::OrderCommentDialog(const char* msg, const char* retmsg, int
h = 850; // Increased from 780 to make room for bigger buttons
// Increase button height for larger keyboard
hh = 110; // Increased from 90 to make buttons bigger

// Change labels for Done and Cancel buttons
if (enterkey)
{
Expand Down Expand Up @@ -3458,78 +3459,87 @@ RenderResult OrderCommentDialog::Render(Terminal *term, int update_flag)
return RENDER_OKAY;
}

// Call parent Render to set up keyboard layout
GetTextDialog::Render(term, update_flag);
first_row_y = y + first_row;

// Layout buttons - reuse GetTextDialog layout code
int kx = 0;
int kw = 0;
// Call DialogZone::Render to set up base dialog
DialogZone::Render(term, update_flag);

int ww = w - (border * 2);
// Start keyboard higher to accommodate clear/space/backspace and done/cancel rows
int ky = y + h - (hh * 6) - border - 8;
int i;
int col = color[0];

// Layout QWERTY keyboard buttons (same as GetTextDialog)
for (i = 0; i < 10; ++i)

// Modern layout with clear spacing
// Text entry area at top: 150 pixels
// Keyboard area: rest of dialog

int entry_area_height = 150;
int keyboard_start_y = y + entry_area_height;
int button_height = 90; // Larger, more touchable buttons
int button_gap = 4;

// ========== TEXT ENTRY AREA ==========
// Title
if (display_string[0] != '\0')
TextC(term, 1, term->Translate(display_string), COLOR_BLACK);

// ========== KEYBOARD LAYOUT ==========
int ky = keyboard_start_y;
int kx = 0;
int kw = 0;

// Row 1: Numbers 1-0 (10 buttons, 9 gaps between them)
int num_button_w = (ww - (9 * button_gap)) / 10;
for (int i = 0; i < 10; ++i)
{
kx = (ww * i * 2) / 21;
kw = ((ww * (i * 2 + 2)) / 21) - kx;
key[i]->SetRegion(x + border + kx, ky, kw, hh);
kx = (num_button_w + button_gap) * i;
key[i]->SetRegion(x + border + kx, ky, num_button_w, button_height);
}

ky += hh;
for (i = 10; i < 20; ++i)

// Row 2: QWERTYUIOP (10 buttons, 9 gaps between them)
ky += button_height + button_gap;
int qwerty_button_w = (ww - (9 * button_gap)) / 10;
for (int i = 10; i < 20; ++i)
{
kx = (ww * (i * 2 - 19)) / 21;
kw = ((ww * (i * 2 - 17)) / 21) - kx;
key[i]->SetRegion(x + border + kx, ky, kw, hh);
kx = (qwerty_button_w + button_gap) * (i - 10);
key[i]->SetRegion(x + border + kx, ky, qwerty_button_w, button_height);
}

ky += hh;
for (i = 20; i < 29; ++i)

// Row 3: ASDFGHJKL (9 buttons, 8 gaps, centered)
ky += button_height + button_gap;
int asdf_button_w = (ww - (8 * button_gap)) / 9;
int asdf_offset = (ww - ((asdf_button_w * 9) + (8 * button_gap))) / 2;
for (int i = 20; i < 29; ++i)
{
kx = (ww * (i * 2 - 38)) / 21;
kw = ((ww * (i * 2 - 36)) / 21) - kx;
key[i]->SetRegion(x + border + kx, ky, kw, hh);
kx = asdf_offset + (asdf_button_w + button_gap) * (i - 20);
key[i]->SetRegion(x + border + kx, ky, asdf_button_w, button_height);
}

ky += hh;
for (i = 29; i < 36; ++i)

// Row 4: ZXCVBNM (7 buttons, 6 gaps, centered)
ky += button_height + button_gap;
int zxcv_button_w = (ww - (6 * button_gap)) / 7;
int zxcv_offset = (ww - ((zxcv_button_w * 7) + (6 * button_gap))) / 2;
for (int i = 29; i < 36; ++i)
{
kx = (ww * (i * 2 - 55)) / 21;
kw = ((ww * (i * 2 - 53)) / 21) - kx;
key[i]->SetRegion(x + border + kx, ky, kw, hh);
kx = zxcv_offset + (zxcv_button_w + button_gap) * (i - 29);
key[i]->SetRegion(x + border + kx, ky, zxcv_button_w, button_height);
}

// Layout space, backspace, clear buttons (above keyboard, same as GetTextDialog)
ky += hh + 4;
kw = ((ww * 6) / 40);
clearkey->SetRegion(x + border, ky, kw, hh);

kx = (ww * 9) / 40;
kw = ((ww * 24) / 40) - kx;
spacekey->SetRegion(x + border + kx, ky, kw, hh);

kx = (ww * 27) / 40;
kw = ((ww * 33) / 40) - kx;
bskey->SetRegion(x + border + kx, ky, kw, hh);

// Layout Done and Cancel buttons at the very bottom (below clear/space/backspace)
ky += hh + 4;
kw = (ww * 18) / 40;
if (enterkey)
enterkey->SetRegion(x + border, ky, kw, hh);

kx = (ww * 22) / 40;
kw = (ww * 18) / 40;
// Row 5: Action buttons (Clear, Space, Backspace) - 3 buttons, 2 gaps
ky += button_height + button_gap;
int action_w = (ww - (2 * button_gap)) / 3;
clearkey->SetRegion(x + border, ky, action_w, button_height);
spacekey->SetRegion(x + border + action_w + button_gap, ky, action_w, button_height);
bskey->SetRegion(x + border + (action_w + button_gap) * 2, ky, action_w, button_height);

// Row 6: Done and Cancel (2 large buttons, 1 gap)
ky += button_height + button_gap;
int bottom_w = (ww - button_gap) / 2;
if (enterkey)
enterkey->SetRegion(x + border, ky, bottom_w, button_height);
if (cancelkey)
cancelkey->SetRegion(x + border + kx, ky, kw, hh);

if (display_string[0] != '\0')
TextC(term, 1, term->Translate(display_string), col);
cancelkey->SetRegion(x + border + bottom_w + button_gap, ky, bottom_w, button_height);

// Render entry field with text (using zone-relative coordinates like GetTextDialog)
RenderEntry(term);

// Render all buttons
buttons.Render(term);

return retval;
Expand All @@ -3548,6 +3558,7 @@ SignalResult OrderCommentDialog::Signal(Terminal *term, const genericChar* messa
{
case 0: // backspace
Backspace(term);
DrawEntry(term); // Ensure display updates
break;
case 1: // clear
buffer[0] = '\0';
Expand Down Expand Up @@ -3601,7 +3612,10 @@ SignalResult OrderCommentDialog::Signal(Terminal *term, const genericChar* messa
break;
default:
if (message[1] == '\0')
{
AddChar(term, message[0]);
DrawEntry(term); // Ensure display updates after adding char
}
break;
}

Expand Down Expand Up @@ -3637,8 +3651,46 @@ SignalResult OrderCommentDialog::Keyboard(Terminal *term, int kb_key, int state)
int OrderCommentDialog::RenderEntry(Terminal *term)
{
FnTrace("OrderCommentDialog::RenderEntry()");
Entry(term, (size_x/2) - 15, 2.5, 30);
TextC(term, 2.5, buffer, COLOR_WHITE);

// Draw entry box at top of dialog using absolute coordinates
// Position it well above the keyboard area
int entry_x = x + border + 20;
int entry_y = y + 80; // Below the title, well above keyboard
int entry_w = w - (border * 2) - 40;
int entry_h = 60;

// Draw the entry box with inset frame - this clears the background
term->RenderFilledFrame(entry_x, entry_y, entry_w, entry_h,
2, IMAGE_DARK_WOOD, FRAME_INSET | FRAME_2COLOR);

// Add cursor to text
genericChar display_text[256];
if (buffidx > 0 && buffer[0] != '\0')
{
vt_safe_string::safe_copy(display_text, 256, buffer);
vt_safe_string::safe_concat(display_text, 256, "_");
}
else
{
vt_safe_string::safe_copy(display_text, 256, "_");
}

// Render text in RED, positioned better within the entry box
int text_x = entry_x + 10;
int text_y = entry_y + 38; // Adjusted for better vertical centering
term->RenderText(display_text, text_x, text_y, COLOR_RED, FONT_TIMES_34);

return 0;
}

int OrderCommentDialog::DrawEntry(Terminal *term)
{
FnTrace("OrderCommentDialog::DrawEntry()");

RenderEntry(term);
// Update the entry box area at top of dialog
term->UpdateArea(x + border, y + 80, w - (border * 2), 60);

return 0;
}

Expand Down
1 change: 1 addition & 0 deletions zone/dialog_zone.hh
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ public:
SignalResult Keyboard(Terminal *term, int kb_key, int state) override;

int RenderEntry(Terminal *term);
int DrawEntry(Terminal *term);
};


Expand Down
8 changes: 4 additions & 4 deletions zone/form_zone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,7 @@ SignalResult ListFormZone::Touch(Terminal *term, int tx, int ty)
if (records <= 0)
return SIGNAL_IGNORED;

FormZone::Touch(term, tx, ty);
LayoutZone::Touch(term, tx, ty);
if (show_list)
{
int new_page = list_page;
Expand Down Expand Up @@ -1137,7 +1137,7 @@ SignalResult ListFormZone::Mouse(Terminal *term, int action, int mx, int my)
return sig;
}

FormZone::Touch(term, mx, my);
LayoutZone::Touch(term, mx, my);
if (show_list)
{
int new_page = list_page;
Expand Down Expand Up @@ -2751,9 +2751,9 @@ SignalResult ListField::Touch(Terminal *term, FormZone *fzone, Flt tx, Flt ty)
FnTrace("ListField::Touch()");
Flt xx = x;
if (label.size() > 0)
xx += label_width + .6;
xx += label_width + 1;

if (tx >= xx && tx <= (xx + entry_width + 1))
if (tx >= xx && tx <= (xx + entry_width))
NextEntry();
return SIGNAL_OKAY;
}
Expand Down
30 changes: 23 additions & 7 deletions zone/user_edit_zone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ SignalResult JobSecurityZone::Touch(Terminal *term, int tx, int ty)
if (records <= 0)
return SIGNAL_IGNORED;

// Update coordinates first
LayoutZone::Touch(term, tx, ty);

// It's bad to disable a job category when there are employee's configured
// for that job. We'll allow it, but only after a confirmation.
Expand All @@ -696,7 +698,13 @@ SignalResult JobSecurityZone::Touch(Terminal *term, int tx, int ty)
term->OpenDialog(sdialog);
}
else
FormZone::Touch(term, tx, ty);
{
if (keyboard_focus &&
keyboard_focus->Touch(term, this, selected_x, selected_y) == SIGNAL_OKAY)
{
UpdateForm(term, record_no);
}
}

Draw(term, 0);
return SIGNAL_OKAY;
Expand All @@ -705,15 +713,23 @@ SignalResult JobSecurityZone::Touch(Terminal *term, int tx, int ty)
SignalResult JobSecurityZone::Mouse(Terminal *term, int action, int mx, int my)
{
FnTrace("JobSecurityZone::Mouse()");
SignalResult retval = SIGNAL_IGNORED;
if (records <= 0 || !(action & MOUSE_PRESS))
return SIGNAL_IGNORED;

// mouse touches are just touches here
if (action & MOUSE_PRESS)
retval = Touch(term, mx, my);

return retval;
// Update coordinates
LayoutZone::Touch(term, mx, my);

keyboard_focus = Find(selected_x, selected_y);
if (keyboard_focus)
{
if (keyboard_focus->Mouse(term, this, action, selected_x, selected_y) == SIGNAL_OKAY)
{
UpdateForm(term, record_no);
}
Draw(term, 0);
return SIGNAL_OKAY;
}
return SIGNAL_IGNORED;
}

int JobSecurityZone::LoadRecord(Terminal *term, int record)
Expand Down