mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-29 17:28:01 +01:00
Fixes culture info of DataField from pr #250
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ obj
|
||||
*.userprefs
|
||||
*~
|
||||
packages
|
||||
.vs
|
||||
.vs
|
||||
*.csproj.user
|
||||
|
||||
@@ -142,8 +142,10 @@ static class Demo {
|
||||
new Button (10, 19, "Cancel"),
|
||||
new TimeField (3, 20, DateTime.Now),
|
||||
new TimeField (23, 20, DateTime.Now, true),
|
||||
new DateField(3, 22, DateTime.Now),
|
||||
new DateField(23, 22, DateTime.Now, true),
|
||||
progress,
|
||||
new Label (3, 22, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar")
|
||||
new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar")
|
||||
|
||||
);
|
||||
|
||||
|
||||
243
Terminal.Gui/Views/DateField.cs
Normal file
243
Terminal.Gui/Views/DateField.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
//
|
||||
// DateField.cs: text entry for date
|
||||
//
|
||||
// Author: Barry Nolte
|
||||
//
|
||||
// Licensed under the MIT license
|
||||
//
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using NStack;
|
||||
|
||||
namespace Terminal.Gui {
|
||||
|
||||
/// <summary>
|
||||
/// Date edit widget
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This widget provides date editing functionality, and mouse support.
|
||||
/// </remarks>
|
||||
public class DateField : TextField {
|
||||
bool isShort;
|
||||
|
||||
int longFieldLen = 10;
|
||||
int shortFieldLen = 8;
|
||||
int FieldLen { get { return isShort ? shortFieldLen : longFieldLen; } }
|
||||
char sepChar; // = '/';
|
||||
string longFormat; // = " MM/dd/yyyy";
|
||||
string shortFormat; // = " MM/dd/yy";
|
||||
string Format { get { return isShort ? shortFormat : longFormat; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor that creates a date edit field at an absolute position and fixed size.
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate.</param>
|
||||
/// <param name="y">The y coordinate.</param>
|
||||
/// <param name="date">Initial date contents.</param>
|
||||
/// <param name="isShort">If true, shows only two digits for the year.</param>
|
||||
public DateField(int x, int y, DateTime date, bool isShort = false) : base(x, y, isShort ? 10 : 12, "")
|
||||
{
|
||||
CultureInfo cultureInfo = CultureInfo.CurrentCulture;
|
||||
sepChar = cultureInfo.DateTimeFormat.DateSeparator.ToCharArray()[0];
|
||||
longFormat = $" {cultureInfo.DateTimeFormat.ShortDatePattern}";
|
||||
shortFormat = GetShortFormat(longFormat);
|
||||
this.isShort = isShort;
|
||||
CursorPosition = 1;
|
||||
Date = date;
|
||||
Changed += DateField_Changed;
|
||||
}
|
||||
|
||||
private void DateField_Changed(object sender, ustring e)
|
||||
{
|
||||
if (!DateTime.TryParseExact(Text.ToString(), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result))
|
||||
Text = e;
|
||||
}
|
||||
|
||||
string GetShortFormat(string lf)
|
||||
{
|
||||
return lf.Replace("yyyy", "yy");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date in the widget.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public DateTime Date {
|
||||
get {
|
||||
if (!DateTime.TryParseExact(Text.ToString(), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result)) return new DateTime();
|
||||
return result;
|
||||
}
|
||||
set {
|
||||
this.Text = value.ToString(Format);
|
||||
}
|
||||
}
|
||||
|
||||
bool SetText(Rune key)
|
||||
{
|
||||
var text = TextModel.ToRunes(Text);
|
||||
var newText = text.GetRange(0, CursorPosition);
|
||||
newText.Add(key);
|
||||
if (CursorPosition < FieldLen)
|
||||
newText = newText.Concat(text.GetRange(CursorPosition + 1, text.Count - (CursorPosition + 1))).ToList();
|
||||
return SetText(ustring.Make(newText));
|
||||
}
|
||||
|
||||
bool SetText(ustring text)
|
||||
{
|
||||
// FIXED: This validation could be made better by calculating the actual min/max values
|
||||
// for month/day/year. This has a 'good' chance of keeping things valid
|
||||
ustring[] vals = text.Split(ustring.Make(sepChar));
|
||||
ustring[] frm = ustring.Make(Format).Split(ustring.Make(sepChar));
|
||||
bool isValidDate = true;
|
||||
int idx = GetFormatIndex(frm, "y");
|
||||
int year = Int32.Parse(vals[idx].ToString());
|
||||
int month;
|
||||
int day;
|
||||
idx = GetFormatIndex(frm, "M");
|
||||
if (Int32.Parse(vals[idx].ToString()) < 1) {
|
||||
isValidDate = false;
|
||||
month = 1;
|
||||
vals[idx] = "1";
|
||||
} else if (Int32.Parse(vals[idx].ToString()) > 12) {
|
||||
isValidDate = false;
|
||||
month = 12;
|
||||
vals[idx] = "12";
|
||||
} else
|
||||
month = Int32.Parse(vals[idx].ToString());
|
||||
idx = GetFormatIndex(frm, "d");
|
||||
if (Int32.Parse(vals[idx].ToString()) < 1) {
|
||||
isValidDate = false;
|
||||
day = 1;
|
||||
vals[idx] = "1";
|
||||
} else if (Int32.Parse(vals[idx].ToString()) > 31) {
|
||||
isValidDate = false;
|
||||
day = DateTime.DaysInMonth(year, month);
|
||||
vals[idx] = day.ToString();
|
||||
} else
|
||||
day = Int32.Parse(vals[idx].ToString());
|
||||
string date = GetData(month, day, year, frm);
|
||||
Text = date;
|
||||
|
||||
if (!DateTime.TryParseExact(date, Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result) ||
|
||||
!isValidDate)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
string GetData(int month, int day, int year, ustring[] fm)
|
||||
{
|
||||
string data = " ";
|
||||
for (int i = 0; i < fm.Length; i++) {
|
||||
if (fm[i].Contains("M"))
|
||||
data += $"{month,2:00}";
|
||||
else if (fm[i].Contains("d"))
|
||||
data += $"{day,2:00}";
|
||||
else
|
||||
data += isShort ? $"{year,2:00}" : $"{year,4:0000}";
|
||||
if (i < 2)
|
||||
data += $"{sepChar}";
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int GetFormatIndex(ustring[] fm, string t)
|
||||
{
|
||||
int idx = -1;
|
||||
for (int i = 0; i < fm.Length; i++) {
|
||||
if (fm[i].Contains(t)) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
void IncCursorPosition()
|
||||
{
|
||||
if (CursorPosition == FieldLen)
|
||||
return;
|
||||
if (Text[++CursorPosition] == sepChar)
|
||||
CursorPosition++;
|
||||
}
|
||||
|
||||
void DecCursorPosition()
|
||||
{
|
||||
if (CursorPosition == 1)
|
||||
return;
|
||||
if (Text[--CursorPosition] == sepChar)
|
||||
CursorPosition--;
|
||||
}
|
||||
|
||||
void AdjCursorPosition()
|
||||
{
|
||||
if (Text[CursorPosition] == sepChar)
|
||||
CursorPosition++;
|
||||
}
|
||||
|
||||
public override bool ProcessKey(KeyEvent kb)
|
||||
{
|
||||
switch (kb.Key) {
|
||||
case Key.DeleteChar:
|
||||
case Key.ControlD:
|
||||
SetText('0');
|
||||
break;
|
||||
|
||||
case Key.Delete:
|
||||
case Key.Backspace:
|
||||
SetText('0');
|
||||
DecCursorPosition();
|
||||
break;
|
||||
|
||||
// Home, C-A
|
||||
case Key.Home:
|
||||
case Key.ControlA:
|
||||
CursorPosition = 1;
|
||||
break;
|
||||
|
||||
case Key.CursorLeft:
|
||||
case Key.ControlB:
|
||||
DecCursorPosition();
|
||||
break;
|
||||
|
||||
case Key.End:
|
||||
case Key.ControlE: // End
|
||||
CursorPosition = FieldLen;
|
||||
break;
|
||||
|
||||
case Key.CursorRight:
|
||||
case Key.ControlF:
|
||||
IncCursorPosition();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore non-numeric characters.
|
||||
if (kb.Key < (Key)((int)'0') || kb.Key > (Key)((int)'9'))
|
||||
return false;
|
||||
if (SetText(TextModel.ToRunes(ustring.Make((uint)kb.Key)).First()))
|
||||
IncCursorPosition();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool MouseEvent(MouseEvent ev)
|
||||
{
|
||||
if (!ev.Flags.HasFlag(MouseFlags.Button1Clicked))
|
||||
return false;
|
||||
if (!HasFocus)
|
||||
SuperView.SetFocus(this);
|
||||
|
||||
var point = ev.X;
|
||||
if (point > FieldLen)
|
||||
point = FieldLen;
|
||||
if (point < 1)
|
||||
point = 1;
|
||||
CursorPosition = point;
|
||||
AdjCursorPosition();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace Terminal.Gui {
|
||||
/// Client code can hook up to this event, it is
|
||||
/// raised when the text in the entry changes.
|
||||
/// </remarks>
|
||||
public event EventHandler Changed;
|
||||
public event EventHandler<ustring> Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor that creates a text field, with layout controlled with X, Y, Width and Height.
|
||||
@@ -98,7 +98,10 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
set {
|
||||
ustring oldText = ustring.Make(text);
|
||||
text = TextModel.ToRunes (value);
|
||||
Changed?.Invoke(this, oldText);
|
||||
|
||||
if (point > text.Count)
|
||||
point = Math.Max (text.Count-1, 0);
|
||||
|
||||
@@ -193,8 +196,6 @@ namespace Terminal.Gui {
|
||||
void SetText (List<Rune> newText)
|
||||
{
|
||||
text = newText;
|
||||
if (Changed != null)
|
||||
Changed (this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
void SetText (IEnumerable<Rune> newText)
|
||||
|
||||
Reference in New Issue
Block a user