Skip to content

Fixes #4621. Replace TimeField/DateField with new TimeEditor/DateEditor based on TextValidateField#4622

Merged
tig merged 46 commits intov2_developfrom
copilot/rewrite-timefield-and-datefield
Mar 9, 2026
Merged

Fixes #4621. Replace TimeField/DateField with new TimeEditor/DateEditor based on TextValidateField#4622
tig merged 46 commits intov2_developfrom
copilot/rewrite-timefield-and-datefield

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 24, 2026

Summary

Replaces the legacy TimeField and DateField controls with new TimeEditor and DateEditor views built on TextValidateField using custom ITextValidateProvider implementations. This provides cleaner architecture, culture-aware formatting, 12-hour AM/PM support, and proper CWP-compliant events.

New Components

TimeTextProvider (ITextValidateProvider)

  • Parses DateTimeFormatInfo.LongTimePattern to detect 12h/24h format, separators, and AM/PM positions
  • Culture-aware formatting with auto-normalization to fixed-width fields (e.g. hhh)
  • Cursor navigation automatically skips separators and AM/PM designators
  • AM/PM toggle via 'A'/'P' keypress for 12-hour formats
  • Auto-corrects invalid time component values
  • CWP-compliant OnTextChanged/TextChanged event pattern

DateTextProvider (ITextValidateProvider)

  • Parses DateTimeFormatInfo.ShortDatePattern for culture-aware date formatting
  • Supports day/month/year in any order with proper separator handling
  • Fixed-width normalization (ddd, MMM, yyyyyy)
  • Auto-correction of invalid dates (e.g. Feb 30 → Feb 28)
  • CWP-compliant OnTextChanged/TextChanged event pattern

TimeEditor (TextValidateField, IValue<TimeSpan>, IDesignable)

  • Delegates validation/formatting to TimeTextProvider
  • CWP-compliant ValueChanging/ValueChanged events with full cancellation support
  • Format property accepts DateTimeFormatInfo for flexible pattern control
  • Width auto-sized based on format length
  • Non-nullable TimeSpan value - consistent with other value types

DateEditor (TextValidateField, IValue<DateTime>, IDesignable)

  • Delegates validation/formatting to DateTextProvider
  • CWP-compliant ValueChanging/ValueChanged events with full cancellation support
  • Format property accepts DateTimeFormatInfo for flexible pattern control
  • Width auto-sized based on format length
  • Non-nullable DateTime value - consistent with TimeEditor and DatePicker

Other Changes

  • DatePicker updated to use DateEditor instead of DateField
    • Fixed initialization: _dateEditor now uses the date parameter instead of DateTime.Now
    • Culture propagation: Culture setter now propagates DateTimeFormatInfo to _dateEditor.Format
    • Value propagation: Value setter now propagates to _dateEditor.Value for proper synchronization
  • CWP compliance applied to OnTextChanged/TextChanged on all four providers (TimeTextProvider, DateTextProvider, NetMaskedTextProvider, TextRegexProvider) — OnTextChanged is now a virtual method for subclass override, with RaiseTextChanged handling event dispatch
  • TextRegexProvider null-safety fix — _text initialized to [] instead of null! to prevent NullReferenceException in Validate() during construction
  • TextValidateField null-guard in OnHasFocusChanged — prevents NullReferenceException when view gains focus before a provider is set
  • UICatalog TimeAndDate scenario updated to demo both TimeEditor and DateEditor
  • Removed: TimeField, DateField, and their tests

Usage

// Time editor with current culture
TimeEditor te = new () { Value = TimeSpan.FromHours (14.5) };
// en-US: " 2:30:00 PM", en-GB: "14:30:00"

// Date editor with current culture
DateEditor de = new () { Value = DateTime.Today };
// en-US: "03/09/2026", de-DE: "09.03.2026"

// Custom format
DateTimeFormatInfo fmt = (DateTimeFormatInfo)CultureInfo.CurrentCulture.DateTimeFormat.Clone ();
fmt.LongTimePattern = "HH:mm";
te.Format = fmt;

// ValueChanging allows cancellation
te.ValueChanging += (_, e) =>
{
    if (e.NewValue.Hours > 18)
    {
        e.Handled = true; // Reject times after 6 PM
    }
};

Architecture

Old New
TimeField 640 lines extending TextField with custom cursor/validation TimeEditor: 185 lines + reusable 450-line provider
DateField ~500 lines extending TextField DateEditor: ~180 lines + reusable ~400-line provider
12-hour AM/PM Not supported Full support via pattern detection
Culture support Separator only, hardcoded format Full DateTimeFormatInfo pattern support
Events Basic CWP-compliant with cancellation
Base class TextField (custom cursor hacks) TextValidateField (native provider pattern)
Value types DateField used DateTime? Both use non-nullable values (TimeSpan, DateTime)
DatePicker integration Manual initialization, no propagation Automatic propagation of Value and Culture/Format to embedded editor

Test Plan

  • 30 TimeEditor/TimeTextProvider tests (cursor, format, 12h/24h, events, cultures)
  • 40 DateEditor/DateTextProvider tests (cursor, format, events, cultures, auto-correction)
  • 10 DatePicker tests including 3 new tests for initialization and Culture/Value propagation
  • All TextValidateField regex provider tests pass (fixed pre-existing null bug)
  • AllViews tests pass for TextValidateField (fixed pre-existing null-guard issue)
  • UnitTests and UnitTestsParallelizable pass
  • Verified on Windows, macOS, Ubuntu

Pull Request checklist:

  • I've named my PR in the form of "Fixes #issue. Terse description."
  • My code follows the style guidelines of Terminal.Gui - if you use Visual Studio, hit CTRL-K-D to automatically reformat your files before committing.
  • My code follows the Terminal.Gui library design guidelines
  • I ran dotnet test before commit
  • I have made corresponding changes to the API documentation (using /// style comments)
  • My changes generate no new warnings
  • I have checked my code and corrected any poor grammar or misspellings
  • I conducted basic QA to assure all features are working

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 4 commits January 24, 2026 14:25
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
…lify logic, extract constants

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Copilot AI changed the title [WIP] Rewrite TimeField and DateField to use TextValidateField Implement TimeEditor and TimeTextProvider based on TextValidateField Jan 24, 2026
Copilot AI requested a review from tig January 24, 2026 14:35
@codecov

This comment was marked as outdated.

@tig

This comment was marked as outdated.

…sWithZero

Co-authored-by: tig <585482+tig@users.noreply.github.com>

This comment was marked as outdated.

@tig

This comment was marked as outdated.

Co-authored-by: tig <585482+tig@users.noreply.github.com>

This comment was marked as off-topic.

Copy link
Copy Markdown
Member

@tig tig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good foundation — the architecture (provider pattern, IValue<TimeSpan>, cultural format support) is sound and the test coverage is a reasonable start. A few issues need to be addressed before merge:

Bugs

  1. Double-firing ValueChanged — Setting Value programmatically fires ValueChanged twice: once via the setter and again via the TextChanged → RaiseValueChangedEvents path triggered by Text = TimeProvider.Text. See inline comment on TimeEditor.cs:55.
  2. ValueChanging not raised for keyboard input — User typing bypasses the cancellable event entirely. See TimeEditor.cs:163.

Code quality
3. Bare catch block in TryManualParse — swallows all exceptions including fatal ones.
4. Width management inconsistency — Constructor uses Dim.Auto, Format setter clobbers it with a fixed integer.
5. Redundant 12-hour adjustment in FormatTimeValue()_isPm may be derivable from _timeValue.Hours rather than tracked as separate state.

Tests
6. Coverage at 61.7% — Below the project's 70% threshold (flagged by CodeCov). The TryManualParse fallback path and much of the cursor navigation code are untested.
7. Missing AI-generated test comment — Project convention requires a comment like // Claude - Sonnet 4.6.
8. Culture-sensitive width test may be non-deterministic.

Minor

  • _separatorPositions: prefer HashSet<int> over List<int> for Contains calls.
  • Trailing whitespace on blank lines in TimeAndDate.cs.

Comment thread Terminal.Gui/Views/TextInput/TimeEditor.cs
Comment thread Terminal.Gui/Views/TextInput/TimeEditor.cs Outdated
Comment thread Terminal.Gui/Views/TextInput/TimeEditor.cs
Comment thread Terminal.Gui/Views/TextInput/TimeTextProvider.cs
Comment thread Terminal.Gui/Views/TextInput/TimeTextProvider.cs
Comment thread Terminal.Gui/Views/TextInput/TimeTextProvider.cs Outdated
Comment thread Tests/UnitTestsParallelizable/Views/TimeEditorTests.cs
Comment thread Tests/UnitTestsParallelizable/Views/TimeEditorTests.cs
Comment thread Tests/UnitTestsParallelizable/Views/TimeEditorTests.cs
tig and others added 11 commits March 7, 2026 15:57
Removed the TimeField control, its tests, and documentation. Introduced TimeEditor as the new time input control, based on TextValidateField with culture-aware formatting. Updated all references, scenarios, and docs to use TimeEditor instead of TimeField. TimeField source and tests have been deleted.
Refactored TimeEditor, TimeTextProvider, NetMaskedTextProvider, and TextRegexProvider for clarity and consistency. Removed the VerifyChar method from ITextValidateProvider and all implementations, simplifying the provider interface. Standardized event raising and improved handling of AM/PM, manual parsing, and pattern normalization in TimeTextProvider. Updated TextValidateField drawing and key handling logic for simplicity. Modernized and clarified all related tests to match the new API and style. Made various code style improvements and updated documentation throughout.
Refactored TextValidateField and TimeEditor to fully implement the IValue<T> interface and follow the Cancellable Work Pattern (CWP) for value changes.
- TextValidateField now exposes Value, ValueChanging, and ValueChanged events, and synchronizes value changes from both programmatic and user input, with support for cancellation and reversion.
- Introduced SuppressValueEvents to prevent unwanted event recursion.
- TimeEditor now uses a private _value field, leverages CWPPropertyHelper for value changes, and raises strongly-typed TimeSpan events.
- Removed manual event wiring and last-known-value tracking in favor of the new base class pattern.
- These changes improve consistency, extensibility, and support for data binding and undo/redo scenarios.
Add DateTextProvider (ITextValidateProvider for dates with culture-aware
formatting, pattern normalization, separator handling) and DateEditor
(extends TextValidateField, implements IValue<DateTime?> with CWP).
Remove legacy DateField and update all usages in DatePicker, scenarios,
docs, and indexes. Full test coverage in DateEditorTests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactored event raising in Date, Time, Masked, and Regex text providers by introducing a private RaiseTextChanged method. OnTextChanged is now virtual and serves as a subclass hook, improving extensibility. Updated all event triggers to use RaiseTextChanged. Improved null safety in TextRegexProvider by initializing _text to an empty list. Fixed CleanSeparator logic in DateTextProvider for short separators. Enhanced TryParseDateValue to only fall back to manual parsing if needed. Updated documentation to clarify new event handling pattern.
@tig tig changed the title Implement TimeEditor and TimeTextProvider based on TextValidateField Fixes #4621. Replace TimeField/DateField with new TimeEditor/DateEditor based on TextValidateField Mar 9, 2026
@tig tig marked this pull request as ready for review March 9, 2026 13:43
@tig tig requested review from BDisp and Copilot March 9, 2026 13:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes Terminal.Gui’s date/time input controls by replacing the legacy TimeField/DateField (TextField-based) with TimeEditor/DateEditor built on TextValidateField + new culture-aware ITextValidateProvider implementations, and updates docs/examples/tests accordingly.

Changes:

  • Added TimeEditor/DateEditor plus TimeTextProvider/DateTextProvider, and removed legacy TimeField/DateField.
  • Updated TextValidateField/providers to use CWP-style value events and support a “blank cursor cell” past the last editable character for fixed-width providers.
  • Updated DatePicker, docs, and UICatalog scenarios to use the new editors; replaced/added unit tests.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
docfx/docs/views.md Updates view docs to reference DateEditor/TimeEditor.
docfx/docs/events.md Updates IValue table entries to DateEditor/TimeEditor.
Tests/UnitTestsParallelizable/Views/TimeFieldTests.cs Removes legacy TimeField tests.
Tests/UnitTestsParallelizable/Views/TimeEditorTests.cs Adds coverage for TimeEditor/TimeTextProvider.
Tests/UnitTestsParallelizable/Views/TextValidateFieldTests.cs Adjusts tests for new blank-cursor-cell behavior and navigation semantics.
Tests/UnitTestsParallelizable/Views/DateFieldTests.cs Removes legacy DateField tests.
Tests/UnitTestsParallelizable/Views/DateEditorTests.cs Adds coverage for DateEditor/DateTextProvider.
Tests/UnitTests/Views/DatePickerTests.cs Updates focus expectations for DateEditor integration.
Terminal.sln.DotSettings Adds spelling exception entry for “TimeEditor”.
Terminal.Gui/Views/TextInput/TimeTextProvider.cs New culture-aware time provider (12h/24h + AM/PM handling).
Terminal.Gui/Views/TextInput/TimeField.cs Removes legacy time input view.
Terminal.Gui/Views/TextInput/TimeEditor.cs New time editor built on TextValidateField with CWP value events.
Terminal.Gui/Views/TextInput/TextValidateField.cs Adds IValue plumbing, provider change tracking, and blank-cursor-cell support.
Terminal.Gui/Views/TextInput/TextRegexProvider.cs Improves null-safety and aligns event raising pattern with other providers.
Terminal.Gui/Views/TextInput/TextEditingLineStatus.cs Cleans up file header/nullability directive.
Terminal.Gui/Views/TextInput/NetMaskedTextProvider.cs Refactors + aligns event raising pattern with other providers.
Terminal.Gui/Views/TextInput/ITextValidateProvider.cs Removes obsolete OnTextChanged method from the interface.
Terminal.Gui/Views/TextInput/DateTextProvider.cs New culture-aware date provider (normalized fixed-width pattern).
Terminal.Gui/Views/TextInput/DateField.cs Removes legacy date input view.
Terminal.Gui/Views/TextInput/DateEditor.cs New date editor built on TextValidateField with CWP value events.
Terminal.Gui/Views/DatePicker.cs Replaces embedded DateField with DateEditor and uses DateTextProvider.NormalizePattern.
Examples/UICatalog/Scenarios/TimeAndDate.cs Updates scenario to demonstrate TimeEditor/DateEditor and formats.
Examples/UICatalog/Scenarios/TextInputControls.cs Replaces DateField/TimeField usage with editors; updates mirroring labels.
AGENTS.md Updates agent index to new TextInput file set.
.tg-docs/INDEX.md Updates docs index to new TextInput file set.

Comment thread Tests/UnitTestsParallelizable/Views/TimeEditorTests.cs
Comment thread Tests/UnitTestsParallelizable/Views/DateEditorTests.cs
Comment thread Terminal.Gui/Views/TextInput/TextValidateField.cs
Comment thread Terminal.Gui/Views/TextInput/DateEditor.cs Outdated
Comment thread Terminal.Gui/Views/DatePicker.cs
Copy link
Copy Markdown
Collaborator

@BDisp BDisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to implement them with TextValidateField.

…and Prompt demo

- Remove Pos.Center() and center text alignment from all editors and labels
- Add inline DatePicker synced bidirectionally with default DateEditor
- Add Prompt<DatePicker> button that opens modal date picker dialog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Copilot finished work on behalf of tig March 9, 2026 14:36
@tig tig merged commit 865c237 into v2_develop Mar 9, 2026
11 checks passed
@tig tig deleted the copilot/rewrite-timefield-and-datefield branch March 9, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants