Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
703bfa0
Initial plan
Copilot Jan 24, 2026
e9ca565
Add TimeTextProvider and TimeEditor implementation
Copilot Jan 24, 2026
dad7634
Add comprehensive unit tests for TimeEditor and TimeTextProvider
Copilot Jan 24, 2026
ba57057
Address code review feedback: fix event args, remove AI comment, simp…
Copilot Jan 24, 2026
181ac3c
Add TimeEditor demonstration to UICatalog TimeAndDate scenario
Copilot Jan 24, 2026
6f31b7c
Fix culture-dependent test failure in TimeTextProvider_Delete_Replace…
Copilot Jan 24, 2026
5dc787f
Fix additional culture-dependent tests for macOS/Windows compatibility
Copilot Jan 24, 2026
0177821
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Jan 25, 2026
33897c1
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 3, 2026
949e788
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 3, 2026
f1de6ce
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 3, 2026
dfbfcbd
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 4, 2026
8c65fd6
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 22, 2026
cd97f9e
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 25, 2026
94cd4f0
Fix code review issues: double-firing events, ValueChanging for keybo…
Copilot Feb 25, 2026
dcdef40
Add comprehensive test coverage for TryManualParse, cursor navigation…
Copilot Feb 25, 2026
da9cd14
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 27, 2026
80f4a3a
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Feb 28, 2026
66fa802
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Mar 4, 2026
f00b6db
TextAlignment = Alignment.End is broken
tig Jan 25, 2026
e7f4169
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 5, 2026
cf40e87
Fix _isPm synchronization in TimeValue setter after f00b6db refactoring
Copilot Mar 6, 2026
124ab29
merged
tig Mar 6, 2026
037f16d
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Mar 6, 2026
9248041
Merge branch 'copilot/rewrite-timefield-and-datefield' of tig:gui-cs/…
tig Mar 6, 2026
d4bdf24
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 7, 2026
e69b34f
Fix TextValidateField test failures and TimeEditor EnableForDesign crash
tig Mar 7, 2026
fe89b5e
Fix TimeEditor cursor/insertion by normalizing time format patterns
tig Mar 7, 2026
9f8d51e
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 7, 2026
100977d
Improve TimeEditor width and TextValidateField cursor logic
tig Mar 7, 2026
75ea987
Improve cursor handling at end of masked text fields
tig Mar 7, 2026
ad7a30f
Replace TimeField with new TimeEditor control
tig Mar 7, 2026
5e7e208
Refactor TimeEditor and providers, remove VerifyChar method
tig Mar 7, 2026
548fe80
Implement CWP and IValue<T> for TextValidateField/TimeEditor
tig Mar 7, 2026
980c453
merged
tig Mar 8, 2026
71c62dd
Replace DateField with new DateEditor based on TextValidateField
tig Mar 8, 2026
8b3a309
code cleanup
tig Mar 9, 2026
8eae61a
code cleanup
tig Mar 9, 2026
68d8c7e
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 9, 2026
ab78380
merged
tig Mar 9, 2026
0a83cc5
Refactor TextChanged event handling for providers
tig Mar 9, 2026
6b0fa2a
Fixed test bug.
tig Mar 9, 2026
dab9d58
Update TimeAndDate scenario: remove center alignment, add DatePicker …
tig Mar 9, 2026
65f935f
Merge branch 'v2_develop' into copilot/rewrite-timefield-and-datefield
tig Mar 9, 2026
5b140f6
Fix DateEditor.Value to be non-nullable (DateTime instead of DateTime?)
Copilot Mar 9, 2026
62e5aa0
Fix DatePicker Culture and Value propagation to embedded DateEditor
Copilot Mar 9, 2026
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
2 changes: 1 addition & 1 deletion .tg-docs/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Instead of embedding descriptions, it points to actual source files that agents
|Views/SpinnerView:{SpinnerStyle.cs,SpinnerView.cs}
|Views/TableView:{CellActivatedEventArgs.cs,CellColorGetterArgs.cs,CellToggledEventArgs.cs,CheckBoxTableSourceWrapper.cs,CheckBoxTableSourceWrapperByIndex.cs,CheckBoxTableSourceWrapperByObject.cs,ColumnStyle.cs,DataTableSource.cs,EnumerableTableSource.cs,IEnumerableTableSource.cs,ITableSource.cs,ListColumnStyle.cs,ListTableSource.cs,RowColorGetterArgs.cs,SelectedCellChangedEventArgs.cs,TableSelection.cs,TableStyle.cs,TableView.CellMapping.cs,TableView.cs,TableView.Drawing.cs,TableView.Mouse.cs,TableView.Navigation.cs,TableView.Selection.cs,TreeTableSource.cs}
|Views/TabView:{Tab.cs,TabChangedEventArgs.cs,TabMouseEventArgs.cs,TabRow.cs,TabStyle.cs,TabView.cs}
|Views/TextInput:{ContentsChangedEventArgs.cs,DateField.cs,HistoryText.cs,HistoryTextItemEventArgs.cs,ITextValidateProvider.cs,NetMaskedTextProvider.cs,TextEditingLineStatus.cs,TextModel.cs,TextRegexProvider.cs,TextValidateField.cs,TimeField.cs}
|Views/TextInput:{ContentsChangedEventArgs.cs,DateEditor.cs,DateTextProvider.cs,HistoryText.cs,HistoryTextItemEventArgs.cs,ITextValidateProvider.cs,NetMaskedTextProvider.cs,TextEditingLineStatus.cs,TextModel.cs,TextRegexProvider.cs,TextValidateField.cs,TimeEditor.cs,TimeTextProvider.cs}
|Views/TextInput/TextField:{TextField.Commands.cs,TextField.cs,TextField.Drawing.cs,TextField.History.cs,TextField.Keyboard.cs,TextField.Mouse.cs,TextField.Selection.cs,TextField.Text.cs,TextFieldAutocomplete.cs}
|Views/TextInput/TextView:{TextView.Commands.cs,TextView.cs,TextView.Drawing.cs,TextView.Files.cs,TextView.Find.cs,TextView.History.cs,TextView.Keyboard.cs,TextView.Mouse.cs,TextView.Movement.cs,TextView.Scrolling.cs,TextView.Selection.cs,TextView.Text.cs,TextView.WordWrap.cs,TextViewAutocomplete.cs,WordWrapManager.cs}
|Views/TreeView:{AspectGetterDelegate.cs,Branch.cs,DelegateTreeBuilder.cs,DrawTreeViewLineEventArgs.cs,ITreeBuilder.cs,ITreeViewFilter.cs,ObjectActivatedEventArgs.cs,SelectionChangedEventArgs.cs,TreeBuilder.cs,TreeNode.cs,TreeNodeBuilder.cs,TreeStyle.cs,TreeView.cs,TreeViewCollectionNavigatorMatcher.cs,TreeViewTextFilter.cs}
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ See `.claude/cookbook/` for common UI patterns:
|Views/SpinnerView:{SpinnerStyle.cs,SpinnerView.cs}
|Views/TableView:{CellActivatedEventArgs.cs,CellColorGetterArgs.cs,CellToggledEventArgs.cs,CheckBoxTableSourceWrapper.cs,CheckBoxTableSourceWrapperByIndex.cs,CheckBoxTableSourceWrapperByObject.cs,ColumnStyle.cs,DataTableSource.cs,EnumerableTableSource.cs,IEnumerableTableSource.cs,ITableSource.cs,ListColumnStyle.cs,ListTableSource.cs,RowColorGetterArgs.cs,SelectedCellChangedEventArgs.cs,TableSelection.cs,TableStyle.cs,TableView.CellMapping.cs,TableView.cs,TableView.Drawing.cs,TableView.Mouse.cs,TableView.Navigation.cs,TableView.Selection.cs,TreeTableSource.cs}
|Views/TabView:{Tab.cs,TabChangedEventArgs.cs,TabMouseEventArgs.cs,TabRow.cs,TabStyle.cs,TabView.cs}
|Views/TextInput:{ContentsChangedEventArgs.cs,DateField.cs,HistoryText.cs,HistoryTextItemEventArgs.cs,ITextValidateProvider.cs,NetMaskedTextProvider.cs,TextEditingLineStatus.cs,TextModel.cs,TextRegexProvider.cs,TextValidateField.cs,TimeField.cs}
|Views/TextInput:{ContentsChangedEventArgs.cs,DateEditor.cs,DateTextProvider.cs,HistoryText.cs,HistoryTextItemEventArgs.cs,ITextValidateProvider.cs,NetMaskedTextProvider.cs,TextEditingLineStatus.cs,TextModel.cs,TextRegexProvider.cs,TextValidateField.cs,TimeEditor.cs,TimeTextProvider.cs}
|Views/TextInput/TextField:{TextField.Commands.cs,TextField.cs,TextField.Drawing.cs,TextField.History.cs,TextField.Keyboard.cs,TextField.Mouse.cs,TextField.Selection.cs,TextField.Text.cs,TextFieldAutocomplete.cs}
|Views/TextInput/TextView:{TextView.Commands.cs,TextView.cs,TextView.Drawing.cs,TextView.Files.cs,TextView.Find.cs,TextView.History.cs,TextView.Keyboard.cs,TextView.Mouse.cs,TextView.Movement.cs,TextView.Scrolling.cs,TextView.Selection.cs,TextView.Text.cs,TextView.WordWrap.cs,TextViewAutocomplete.cs,WordWrapManager.cs}
|Views/TreeView:{AspectGetterDelegate.cs,Branch.cs,DelegateTreeBuilder.cs,DrawTreeViewLineEventArgs.cs,ITreeBuilder.cs,ITreeViewFilter.cs,ObjectActivatedEventArgs.cs,SelectionChangedEventArgs.cs,TreeBuilder.cs,TreeNode.cs,TreeNodeBuilder.cs,TreeStyle.cs,TreeView.cs,TreeViewCollectionNavigatorMatcher.cs,TreeViewTextFilter.cs}
Expand Down
131 changes: 60 additions & 71 deletions Examples/UICatalog/Scenarios/TextInputControls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("DateTime")]
public class TextInputControls : Scenario
{
private Label? _labelMirroringTimeField;
private TimeField? _timeField;
private Label? _labelMirroringTimeEditor;
private TimeEditor? _timeEditor;

public override void Main ()
{
Expand Down Expand Up @@ -115,10 +115,7 @@ void TextViewDrawContent (object? sender, DrawEventArgs e) =>

CheckBox chxReadOnly = new ()
{
X = Pos.Left (textView),
Y = Pos.Bottom (textView),
Value = textView.ReadOnly ? CheckState.Checked : CheckState.UnChecked,
Text = "Read_Only"
X = Pos.Left (textView), Y = Pos.Bottom (textView), Value = textView.ReadOnly ? CheckState.Checked : CheckState.UnChecked, Text = "Read_Only"
};
chxReadOnly.ValueChanging += (_, args) => textView.ReadOnly = args.NewValue == CheckState.Checked;
win.Add (chxReadOnly);
Expand Down Expand Up @@ -155,39 +152,39 @@ void TextViewDrawContent (object? sender, DrawEventArgs e) =>
};

chxMultiline.ValueChanging += (_, e) =>
{
textView.Multiline = e.NewValue == CheckState.Checked;
{
textView.Multiline = e.NewValue == CheckState.Checked;

if (!textView.Multiline && chxWordWrap.Value == CheckState.Checked)
{
chxWordWrap.Value = CheckState.UnChecked;
}
if (!textView.Multiline && chxWordWrap.Value == CheckState.Checked)
{
chxWordWrap.Value = CheckState.UnChecked;
}

if (!textView.Multiline && chxCaptureTabs.Value == CheckState.Checked)
{
chxCaptureTabs.Value = CheckState.UnChecked;
}
};
if (!textView.Multiline && chxCaptureTabs.Value == CheckState.Checked)
{
chxCaptureTabs.Value = CheckState.UnChecked;
}
};

Key? keyTab = textView.KeyBindings.GetFirstFromCommands (Command.NextTabStop);
Key? keyBackTab = textView.KeyBindings.GetFirstFromCommands (Command.PreviousTabStop);

chxCaptureTabs.ValueChanging += (_, e) =>
{
textView.TabKeyAddsTab = e.NewValue == CheckState.Checked;

// TODO: This should be in TextView.TabKeyAddsTab_set
if (e.NewValue == CheckState.Checked)
{
textView.KeyBindings.Add (keyTab!, Command.NextTabStop);
textView.KeyBindings.Add (keyBackTab!, Command.PreviousTabStop);
}
else
{
textView.KeyBindings.Remove (keyTab!);
textView.KeyBindings.Remove (keyBackTab!);
}
};
{
textView.TabKeyAddsTab = e.NewValue == CheckState.Checked;

// TODO: This should be in TextView.TabKeyAddsTab_set
if (e.NewValue == CheckState.Checked)
{
textView.KeyBindings.Add (keyTab!, Command.NextTabStop);
textView.KeyBindings.Add (keyBackTab!, Command.PreviousTabStop);
}
else
{
textView.KeyBindings.Remove (keyTab!);
textView.KeyBindings.Remove (keyBackTab!);
}
};
win.Add (chxCaptureTabs);

CheckBox scrollBars = new ()
Expand Down Expand Up @@ -227,56 +224,48 @@ void TextViewDrawContent (object? sender, DrawEventArgs e) =>
};
win.Add (labelMirroringHexEditor);

// DateField
label = new Label { Text = " _DateField:", Y = Pos.Bottom (hexEditor) + 1 };
// DateEditor
label = new Label { Text = "_DateEditor:", Y = Pos.Bottom (hexEditor) + 1 };
win.Add (label);

DateField dateField = new (DateTime.Now) { X = Pos.Right (label) + 1, Y = Pos.Bottom (hexEditor) + 1, Width = 20 };
win.Add (dateField);
DateEditor dateEditor = new () { X = Pos.Right (label) + 1, Y = Pos.Bottom (hexEditor) + 1, Value = DateTime.Today };
win.Add (dateEditor);

Label labelMirroringDateField = new ()
Label labelMirroringDateEditor = new ()
{
X = Pos.Right (dateField) + 1,
Y = Pos.Top (dateField),
Width = Dim.Width (dateField),
Height = Dim.Height (dateField),
Text = dateField.Text
X = Pos.Right (dateEditor) + 1,
Y = Pos.Top (dateEditor),
Width = Dim.Width (dateEditor),
Height = Dim.Height (dateEditor),
Text = dateEditor.Text
};
win.Add (labelMirroringDateField);
win.Add (labelMirroringDateEditor);

dateField.TextChanged += (_, _) => { labelMirroringDateField.Text = dateField.Text; };
dateEditor.ValueChanged += (_, _) => { labelMirroringDateEditor.Text = dateEditor.Text; };

// TimeField
label = new Label { Text = "T_imeField:", Y = Pos.Top (dateField), X = Pos.Right (labelMirroringDateField) + 5 };
// TimeEditor
label = new Label { Text = "T_imeEditor:", Y = Pos.Top (dateEditor), X = Pos.Right (labelMirroringDateEditor) + 5 };
win.Add (label);

_timeField = new TimeField
{
X = Pos.Right (label) + 1,
Y = Pos.Top (dateField),
Width = 20,
IsShortFormat = false,
Value = DateTime.Now.TimeOfDay
};
win.Add (_timeField);
_timeEditor = new TimeEditor { X = Pos.Right (label) + 1, Y = Pos.Top (dateEditor), Value = DateTime.Now.TimeOfDay };
win.Add (_timeEditor);

_labelMirroringTimeField = new Label
_labelMirroringTimeEditor = new Label
{
X = Pos.Right (_timeField) + 1,
Y = Pos.Top (_timeField),
Width = Dim.Width (_timeField),
Height = Dim.Height (_timeField),
Text = _timeField.Text
X = Pos.Right (_timeEditor) + 1,
Y = Pos.Top (_timeEditor),
Width = Dim.Width (_timeEditor),
Height = Dim.Height (_timeEditor),
Text = _timeEditor.Text
};
win.Add (_labelMirroringTimeField);
win.Add (_labelMirroringTimeEditor);

_timeField.ValueChanged += TimeChanged;
_timeEditor.ValueChanged += TimeChanged;

// MaskedTextProvider - uses .NET MaskedTextProvider
Label netProviderLabel = new () { X = Pos.Left (dateField), Y = Pos.Bottom (dateField) + 1, Text = "_NetMaskedTextProvider [ +99 (000) 000-0000 ]:" };
win.Add (netProviderLabel);

NetMaskedTextProvider netProvider = new ("+99 (000) 000-0000");
Label netProviderLabel = new () { X = Pos.Left (dateEditor), Y = Pos.Bottom (dateEditor) + 1, Text = $"_NetMaskedTextProvider ({netProvider.Mask}):" };
win.Add (netProviderLabel);

TextValidateField netProviderField = new () { X = Pos.Right (netProviderLabel) + 1, Y = Pos.Y (netProviderLabel), Provider = netProvider };
win.Add (netProviderField);
Expand All @@ -294,14 +283,14 @@ void TextViewDrawContent (object? sender, DrawEventArgs e) =>
netProviderField.Provider.TextChanged += (_, _) => { labelMirroringNetProviderField.Text = netProviderField.Text; };

// TextRegexProvider - Regex provider implemented by Terminal.Gui
TextRegexProvider provider2 = new ("^([0-9]?[0-9]?[0-9]|1000)$") { ValidateOnInput = false };

Label regexProviderLabel = new ()
{
X = Pos.Left (netProviderLabel), Y = Pos.Bottom (netProviderLabel) + 1, Text = "Text_RegexProvider [ ^([0-9]?[0-9]?[0-9]|1000)$ ]:"
X = Pos.Left (netProviderLabel), Y = Pos.Bottom (netProviderLabel) + 1, Text = $"Text_RegexProvider ({provider2.Pattern}):"
};
win.Add (regexProviderLabel);

TextRegexProvider provider2 = new ("^([0-9]?[0-9]?[0-9]|1000)$") { ValidateOnInput = false };

TextValidateField regexProviderField = new ()
{
X = Pos.Right (regexProviderLabel) + 1,
Expand Down Expand Up @@ -475,9 +464,9 @@ void WinOnAccept (object? sender, CommandEventArgs e)

private void TimeChanged (object? sender, ValueChangedEventArgs<TimeSpan> e)
{
if (_labelMirroringTimeField is { } && _timeField is { })
if (_labelMirroringTimeEditor is { } && _timeEditor is { })
{
_labelMirroringTimeField.Text = _timeField.Text;
_labelMirroringTimeEditor.Text = _timeEditor.Text;
}
}
}
Loading
Loading