LCL/TTimeEdit: Add new properties TimeFormat and TimeSeparator for more flexibility in formatting.

This commit is contained in:
wp_xyz 2024-02-25 18:54:22 +01:00
parent 8e1fc0cf60
commit d85a691fb1

View File

@ -726,6 +726,8 @@ type
FDefaultNow: Boolean; FDefaultNow: Boolean;
FDroppedDown: Boolean; FDroppedDown: Boolean;
FSimpleLayout: Boolean; FSimpleLayout: Boolean;
FTimeFormat: String;
FTimeSeparator: String;
FOnAcceptTime: TAcceptTimeEvent; FOnAcceptTime: TAcceptTimeEvent;
FOnCustomTime: TCustomTimeEvent; FOnCustomTime: TCustomTimeEvent;
function GetTime: TDateTime; function GetTime: TDateTime;
@ -733,6 +735,8 @@ type
procedure SetEmptyTime; procedure SetEmptyTime;
function GetLayout: Boolean; function GetLayout: Boolean;
procedure SetLayout(AValue: Boolean); procedure SetLayout(AValue: Boolean);
procedure SetTimeFormat(AValue: String);
procedure SetTimeSeparator(AValue: String);
procedure TimePopupReturnTime(Sender: TObject; const ATime: TDateTime); procedure TimePopupReturnTime(Sender: TObject; const ATime: TDateTime);
procedure TimePopupShowHide(Sender: TObject); procedure TimePopupShowHide(Sender: TObject);
procedure OpenTimePopup; procedure OpenTimePopup;
@ -740,11 +744,15 @@ type
function TryParseInput(AInput: String; out ParseResult: TDateTime): Boolean; function TryParseInput(AInput: String; out ParseResult: TDateTime): Boolean;
protected protected
function GetDefaultGlyphName: string; override; function GetDefaultGlyphName: string; override;
function UsedFormatSettings: TFormatSettings;
function UsedTimeFormat: String;
function UsedTimeSeparator: Char;
procedure ButtonClick; override; procedure ButtonClick; override;
procedure EditDblClick; override; procedure EditDblClick; override;
procedure EditEditingDone; override; procedure EditEditingDone; override;
public public
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
function ValidTimeFormat(AFormat: String): Boolean;
property Time: TDateTime read GetTime write SetTime; property Time: TDateTime read GetTime write SetTime;
property Button; property Button;
property DroppedDown: Boolean read FDroppedDown; property DroppedDown: Boolean read FDroppedDown;
@ -793,6 +801,8 @@ type
property Spacing; property Spacing;
property TabStop; property TabStop;
property TabOrder; property TabOrder;
property TimeFormat: String read FTimeFormat write SetTimeFormat;
property TimeSeparator: String read FTimeSeparator write SetTimeSeparator;
property Visible; property Visible;
property Text; property Text;
property TextHint; property TextHint;
@ -2185,6 +2195,7 @@ begin
RegisterPropertyToSkip(TDateEdit, 'CancelCaption', 'Property streamed in older Lazarus revision',''); RegisterPropertyToSkip(TDateEdit, 'CancelCaption', 'Property streamed in older Lazarus revision','');
end; end;
{ TTimeEdit } { TTimeEdit }
function TTimeEdit.GetTime: TDateTime; function TTimeEdit.GetTime: TDateTime;
@ -2214,15 +2225,30 @@ begin
end; end;
procedure TTimeEdit.SetTime(AValue: TDateTime); procedure TTimeEdit.SetTime(AValue: TDateTime);
var
Output: String;
begin begin
DateTimeToString(Output, DefaultFormatSettings.ShortTimeFormat, AValue); Text := FormatDateTime(UsedTimeFormat, AValue, UsedFormatSettings);
Text := Output;
FTime := AValue; FTime := AValue;
IsEmptyTime := False; IsEmptyTime := False;
end; end;
procedure TTimeEdit.SetTimeFormat(AValue: String);
begin
if (FTimeFormat <> AValue) and ValidTimeFormat(AValue) then
begin
FTimeFormat := AValue;
SetTime(FTime);
end;
end;
procedure TTimeEdit.SetTimeSeparator(AValue: String);
begin
if FTimeSeparator <> AValue then
begin
FTimeSeparator := AValue;
SetTime(FTime);
end;
end;
procedure TTimeEdit.SetEmptyTime; procedure TTimeEdit.SetEmptyTime;
begin begin
Text := EmptyStr; Text := EmptyStr;
@ -2268,12 +2294,15 @@ begin
end; end;
function TTimeEdit.TryParseInput(AInput: String; out ParseResult: TDateTime): Boolean; function TTimeEdit.TryParseInput(AInput: String; out ParseResult: TDateTime): Boolean;
var
timeSep: Char;
begin begin
AInput := Trim(AInput); AInput := Trim(AInput);
if (Length(AInput) in [3..4]) and (not AnsiContainsStr(AInput, DefaultFormatSettings.TimeSeparator)) then begin timeSep := UsedTimeSeparator;
Insert(DefaultFormatSettings.TimeSeparator, AInput, Length(AInput) - 1); if (Length(AInput) in [3..4]) and (not AnsiContainsStr(AInput, timeSep)) then begin
Insert(timeSep, AInput, Length(AInput) - 1);
end; end;
Result := TryStrToTime(AInput, ParseResult); Result := TryStrToTime(AInput, ParseResult, UsedFormatSettings);
end; end;
procedure TTimeEdit.ParseInput; procedure TTimeEdit.ParseInput;
@ -2293,6 +2322,106 @@ begin
Result := ResBtnTime; Result := ResBtnTime;
end; end;
function TTimeEdit.UsedFormatSettings: TFormatSettings;
begin
Result := DefaultFormatSettings;
Result.TimeSeparator := UsedTimeSeparator;
Result.ShortTimeFormat := UsedTimeFormat;
end;
function TTimeEdit.UsedTimeFormat: String;
begin
if FTimeFormat = '' then
Result := DefaultFormatSettings.ShortTimeFormat
else
Result := FTimeFormat;
end;
function TTimeEdit.UsedTimeSeparator: Char;
begin
if FTimeSeparator = '' then
Result := DefaultFormatSettings.TimeSeparator
else
Result := FTimeSeparator[1];
end;
// Since TTimeEdit requires a pure "time format string" this function checks
// whether AFormat contains date parts and returns FALSE in this case.
function TTimeEdit.ValidTimeFormat(AFormat: String): Boolean;
type
TFormatPart = (pH, pM, pN, pS, pZ);
var
p, pEnd: PChar;
parts: set of TFormatPart = [];
quoteChar: Char;
begin
if AFormat = '' then
exit(true);
Result := false;
p := @AFormat[1];
pEnd := p + Length(AFormat);
while p < pEnd do begin
case p^ of
// allowed
'h', 'H':
Include(parts, pH);
'n', 'N':
begin
Include(parts, pN);
if (pM in parts) then exit; // mixing of 'n' and 'm' not valid
end;
's', 'S':
begin
Include(parts, pS);
if parts * [pH, pM, pS] = [pM, pS] then exit; // m:s not valid
end;
'z', 'Z':
begin
Include(parts, pZ);
if parts * [pH, pM, pS, pZ] = [pM, pZ] then exit; // m:z is not valid
end;
'm', 'M':
begin
Include(parts, pM);
if (pN in parts) then exit; // mixing of 'n' and 'm' not valid
if parts * [pH, pM, pS] = [pM, pS] then exit; // m:s not valid
if parts * [pH, pM, pS, pZ] = [pM, pZ] then exit; // m.z not valid
end;
// ignore quoted parts
'"', '''':
begin
quotechar := p^;
inc(p);
while (p < pEnd) and (p^ <> quoteChar) do
inc(p);
end;
// am/pm
'a', 'A':
begin
if (StrLIComp(p, 'A/P', 3) = 0) then
inc(p, 2)
else
if (StrLIComp(P, 'AMPM', 4) = 0) then
inc(p, 3)
else
if (StrLIComp(P, 'AM/PM', 5) = 0) then
inc(p, 4)
else
exit;
end;
'[', ']':
exit; // interval mode does not fit to the GUI of this control
'd', 'D', 'y', 'Y':
exit; // parts of date format
else
; // all other characters allowed (not necessariy optimal)
end;
inc(p);
end;
Result := true;
end;
procedure TTimeEdit.ButtonClick; procedure TTimeEdit.ButtonClick;
begin begin
inherited ButtonClick; inherited ButtonClick;
@ -2318,6 +2447,7 @@ begin
FSimpleLayout := True; FSimpleLayout := True;
end; end;
{ TCalcEdit } { TCalcEdit }
function TCalcEdit.GetAsFloat: Double; function TCalcEdit.GetAsFloat: Double;