lazarus/lcl/include/customupdown.inc

754 lines
19 KiB
PHP

{%MainUnit ../comctrls.pp}
{ TCustomUpDown
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Problems -
- Doesn't draw Themed Arrows/doesn't match system colors
- Associate Key down and Tabbing(VK_Up, VK_Down)
}
Type
{ TUpDownButton }
TUpDownButton = Class(TSpeedButton)
private
FMouseTimer : TTimer;
FUpDown : TCustomUpDown;
FButtonType : TUDBtnType;
private
procedure ButtonCoordToUpDownCoord(var X,Y: Integer);
protected
procedure MouseEnter; override;
procedure MouseLeave; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure DblClick; override;
public
constructor CreateWithParams(UpDown : TCustomUpDown; ButtonType : TUDBtnType);
procedure Click; override;
procedure Paint; override;
end;
procedure TUpDownButton.ButtonCoordToUpDownCoord(var X, Y: Integer);
begin
//Fix me: there must be a better way to do this?
//Result may be off by appr. 2 pixels
if FUpDown.Orientation = udVertical then
begin
if FButtonType = btPrev then //down arrow
Y := Y + Height; //assumes both buttons are equal height
end
else
begin
if FButtonType = btNext then //right arrow
X := X + Width;
end;
end;
procedure TUpDownButton.MouseEnter;
begin
inherited MouseEnter; //does UpdateState o.a., so call this as well
FUpDown.MouseEnter;
end;
procedure TUpDownButton.MouseLeave;
begin
inherited MouseLeave; //does UpdateState o.a., so call this as well
FUpDown.MouseLeave;
end;
procedure TUpDownButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseDown(Button, Shift, X, Y);
ButtonCoordToUpDownCoord(X, Y);
FUpDown.MouseDown(Button, Shift, X, Y);
if Button = mbLeft then
begin
FUpDown.FMouseTimerEvent := @Self.Click;
FUpDown.FMouseDownBounds := Bounds(ClientOrigin.X, ClientOrigin.Y, Width, Height);
If Not Assigned(FMouseTimer) then
FMouseTimer := TTimer.Create(FUpDown);
With FMouseTimer do
begin
Enabled := False;
Interval := 300;
OnTimer := @FUpDown.BTimerExec;
Enabled := True;
end;
end;
end;
procedure TUpDownButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
ButtonCoordToUpDownCoord(X, Y);
FUpDown.MouseUp(Button, Shift, X, Y);
If Assigned(FMouseTimer) then
begin
FreeAndNil(FMouseTimer);
FUpDown.FMouseDownBounds := Rect(0,0,0,0);
FUpDown.FMouseTimerEvent := nil;
end;
end;
procedure TUpDownButton.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
inherited MouseMove(Shift, X, Y);
ButtonCoordToUpDownCoord(X, Y);
FUpDown.MouseMove(Shift, X, Y);
end;
procedure TUpDownButton.DblClick;
begin
Click;
end;
procedure TUpDownButton.Click;
begin
FUpDown.Click(FButtonType);
end;
constructor TUpDownButton.CreateWithParams(UpDown : TCustomUpDown;
ButtonType : TUDBtnType);
begin
Inherited Create(UpDown);
FUpDown := UpDown;
FButtonType := ButtonType;
Parent := FUpDown;
ControlStyle := ControlStyle + [csNoFocus, csNoDesignSelectable];
end;
procedure TUpDownButton.Paint;
var
tmp : double;
ax, ay, ah, aw : integer;
j : integer;
begin
Inherited Paint;
if Enabled then
Canvas.Pen.Color := clBtnText //Not perfect, but it works
else
Canvas.Pen.Color := clGrayText;
ah := height div 2;
aw := width div 2;
if (FUpDown.Orientation = udHorizontal) then begin
tmp := double(ah+1)/2;
if (tmp > aw) then begin
ah := 2*aw - 1;
aw := (ah+1) div 2;
end
else begin
aw := RoundToInt(tmp);
ah := 2*aw - 1;
end;
aw := max(aw, 3);
ah := max(ah, 5);
end
else begin
tmp := double(aw+1)/2;
if (tmp > ah) then begin
aw := 2*ah - 1;
ah := (aw+1) div 2;
end
else begin
ah := RoundToInt(tmp);
aw := 2*ah - 1;
end;
ah := max(ah, 3);
aw := max(aw, 5);
end;
ax := (width - aw) div 2;
ay := (height - ah) div 2;
Case FButtonType of
btPrev :
begin
If FUpDown.Orientation = udVertical then begin
for j := 0 to aw div 2 do begin
Canvas.MoveTo(ax + j, ay + j);
Canvas.LineTo(ax + aw - j, ay + j);
end;
end
else
for j := 0 to ah div 2 do begin
Canvas.MoveTo(ax + aw - j - 2, ay + j);
Canvas.LineTo(ax + aw - j - 2, ay + ah - j - 1);
end;
end;
btNext :
begin
If FUpDown.Orientation = udVertical then begin
for j := 0 to aw div 2 do begin
Canvas.MoveTo(ax + j, ay + ah - j - 1);
Canvas.LineTo(ax + aw - j, ay + ah - j - 1);
end;
end
else
for j := 0 to ah div 2 do begin
Canvas.MoveTo(ax + j, ay + j);
Canvas.LineTo(ax + j, ay + ah - j - 1);
end;
end
end;
end;
{ TCustomUpDown }
constructor TCustomUpDown.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle - [csDoubleClicks] +
[csClickEvents, csOpaque, csReplicatable, csNoFocus];
FUseWS := IsWSComponentInheritsFrom(TCustomUpDown, TWSCustomUpDown);
FOrientation := udVertical;
if not FUseWS then begin
FMinBtn := TUpDownButton.CreateWithParams(Self, btPrev);
FMaxBtn := TUpDownButton.CreateWithParams(Self, btNext);
end;
with GetControlClassDefaultSize do
SetInitialBounds(0, 0, CX, CY);
FArrowKeys := True;
FMax := 100;
FMinRepeatInterval := 100;
FIncrement := 1;
FAlignButton := udRight;
FThousands := True;
end;
destructor TCustomUpDown.Destroy;
begin
FAssociate := nil;
inherited destroy;
end;
procedure TCustomUpDown.BTimerExec(Sender : TObject);
var
AInterval: Integer;
begin
If Assigned(FMouseTimerEvent) and PtInRect(FMouseDownBounds, Mouse.CursorPos) then
begin
AInterval := TTimer(Sender).Interval;
if AInterval > FMinRepeatInterval then begin
AInterval := AInterval - 25;
if AInterval < FMinRepeatInterval then AInterval := FMinRepeatInterval;
TTimer(Sender).Interval := AInterval;
end;
FMouseTimerEvent;
end;
end;
procedure TCustomUpDown.UpdateUpDownPositionText;
begin
if (not (csDesigning in ComponentState)) and (FAssociate <> nil)
then begin
if Thousands
then FAssociate.Caption := FloatToStrF(FPosition, ffNumber, 0, 0)
else FAssociate.Caption := IntToStr(FPosition);
end;
end;
class procedure TCustomUpDown.WSRegisterClass;
begin
inherited WSRegisterClass;
RegisterCustomUpDown;
end;
procedure TCustomUpDown.InitializeWnd;
begin
inherited InitializeWnd;
if not FUseWS then Exit;
TWSCustomUpDownClass(WidgetSetClass).SetMinPosition(Self, FMin);
TWSCustomUpDownClass(WidgetSetClass).SetMaxPosition(Self, FMax);
TWSCustomUpDownClass(WidgetSetClass).SetPosition(Self, FPosition);
TWSCustomUpDownClass(WidgetSetClass).SetIncrement(Self, FIncrement);
TWSCustomUpDownClass(WidgetSetClass).SetWrap(Self, FWrap);
TWSCustomUpDownClass(WidgetSetClass).SetOrientation(Self, FOrientation);
TWSCustomUpDownClass(WidgetSetClass).SetUseArrowKeys(Self, FArrowKeys);
end;
procedure TCustomUpDown.UpdateOrientation;
var
d, r: Integer;
begin
if FUseWS then Exit;
If FOrientation = udHorizontal then begin
d:=ClientWidth div 2;
r:=ClientWidth mod 2;
FMinBtn.SetBounds(0,0,d,ClientHeight);
FMaxBtn.SetBounds(d+r,0,d,ClientHeight);
end
else begin
d:=ClientHeight div 2;
r:=ClientHeight mod 2;
FMaxBtn.SetBounds(0,0,ClientWidth,d);
FMinBtn.SetBounds(0,d+r,ClientWidth,d);
end;
end;
procedure TCustomUpDown.UpdateAlignButtonPos;
var
NewWidth: Integer;
NewLeft: Integer;
NewHeight: Integer;
NewTop: Integer;
begin
If Assigned(Associate) then begin
if FAlignButton in [udLeft,udRight] then begin
NewWidth := Width;
NewHeight := Associate.Height;
If FAlignButton = udLeft then
NewLeft := Associate.Left - NewWidth
else
NewLeft := Associate.Left + Associate.Width;
NewTop := Associate.Top;
end else begin
NewWidth := Associate.Width;
NewHeight := Height;
NewLeft := Associate.Left;
If FAlignButton = udTop then
NewTop := Associate.Top - NewHeight
else
NewTop := Associate.Top + Associate.Height;
end;
SetBounds(NewLeft,NewTop,NewWidth,NewHeight);
end;
end;
function TCustomUpDown.CanChange: Boolean;
begin
Result := True;
if Assigned(FOnChanging) then
FOnChanging(Self, Result);
if Assigned(FOnChangingEx) then
FOnChangingEx(Self, Result, FCanChangePos, FCanChangeDir);
end;
procedure TCustomUpDown.Click(Button: TUDBtnType);
begin
FCanChangePos := Position;
FCanChangeDir := updNone;
case Button of
btPrev :
begin
FCanChangeDir := updDown;
if FCanChangePos - Increment >= Min then
FCanChangePos := FCanChangePos - Increment
else
if Wrap then
FCanChangePos := Max + (FCanChangePos - Increment - Min) + 1
else
FCanChangePos := Min;
end;
btNext :
begin
FCanChangeDir := updUp;
if FCanChangePos + Increment <= Max then
FCanChangePos := FCanChangePos + Increment
else
If Wrap then
FCanChangePos := Min + (FCanChangePos + Increment - Max) - 1
else
FCanChangePos := Max;
end;
end;
if not CanChange then Exit;
Position := FCanChangePos;
if Assigned(FOnClick) then
FOnClick(Self, Button);
end;
procedure TCustomUpDown.SetAssociate(Value: TWinControl);
var
I: Integer;
OtherControl: TControl;
begin
// check that no other updown component is associated to the new Associate
if (Value <> FAssociate) and (Value<>nil) then
for I := 0 to Parent.ControlCount - 1 do begin
OtherControl:=Parent.Controls[I];
if (OtherControl is TCustomUpDown) and (OtherControl <> Self) then
if TCustomUpDown(OtherControl).Associate = Value then
raise Exception.CreateFmt(rsIsAlreadyAssociatedWith,
[Value.Name,OtherControl.Name]);
end;
// disconnect old Associate
if FAssociate <> nil then
begin
FAssociate.RemoveAllHandlersOfObject(Self);
FAssociate := nil;
end;
// connect new Associate
if (Value <> nil) and (Value.Parent = Self.Parent)
and not (Value is TCustomUpDown) and not (Value is TCustomTreeView)
and not (Value is TCustomListView)
then
begin
FAssociate := Value;
UpdateUpDownPositionText;
UpdateAlignButtonPos;
FAssociate.AddHandlerOnKeyDown(@AssociateKeyDown,true);
FAssociate.AddHandlerOnChangeBounds(@OnAssociateChangeBounds,true);
FAssociate.AddHandlerOnEnabledChanged(@OnAssociateChangeEnabled,true);
FAssociate.AddHandlerOnVisibleChanged(@OnAssociateChangeVisible,true);
FAssociate.AddHandlerOnMouseWheel(@AssociateMouseWheel,true);
end;
end;
procedure TCustomUpDown.AdjustPos(incPos: Boolean);
var
anewpos: Integer;
begin
if FUseWS then begin
if incPos then anewpos := Position + Increment
else anewpos := Position - Increment;
if (anewpos < Min) then anewpos := Min
else if (anewpos > Max) then anewpos := Max;
SetPosition(anewpos);
end else begin
if incPos then TCustomSpeedButton(FMaxBtn).Click
else TCustomSpeedButton(FMinBtn).Click;
end;
end;
procedure TCustomUpDown.AssociateKeyDown(Sender: TObject; var Key: Word;
ShiftState : TShiftState);
var
ConsumeKey: Boolean;
begin
ConsumeKey := False;
if ArrowKeys and (ShiftState = []) then
begin
case FOrientation of
udVertical:
case Key of
VK_Up:
begin
AdjustPos(True);
ConsumeKey := True;
end;
VK_Down:
begin
AdjustPos(False);
ConsumeKey := True;
end;
end;
udHorizontal:
case Key of
VK_Left:
begin
AdjustPos(False);
ConsumeKey := True;
end;
VK_Right:
begin
AdjustPos(True);
ConsumeKey := True;
end;
end;
end;
end;
if ConsumeKey then
Key := 0;
end;
procedure TCustomUpDown.AssociateMouseWheel(Sender: TObject;
Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
var Handled: Boolean);
begin
//debugln('TCustomUpDown.AssociateMouseWheel A: Handled = ',DbgS(Handled));
if (WheelDelta > 0) then
begin
AdjustPos(True);
Handled := True;
end
else if (WheelDelta < 0) then
begin
AdjustPos(False);
Handled := True;
end;
//debugln('TCustomUpDown.AssociateMouseWheel End: Handled = ',DbgS(Handled));
end;
procedure TCustomUpDown.OnAssociateChangeBounds(Sender: TObject);
begin
UpdateAlignButtonPos;
end;
procedure TCustomUpDown.OnAssociateChangeEnabled(Sender: TObject);
begin
if Assigned(FAssociate) then
SetEnabled(FAssociate.Enabled);
end;
procedure TCustomUpDown.OnAssociateChangeVisible(Sender: TObject);
begin
if Assigned(FAssociate) then
SetVisible(FAssociate.Visible);
end;
function TCustomUpDown.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
Result := inherited DoMouseWheelDown(Shift, MousePos);
if not Result and not FUseWS then
TCustomSpeedButton(FMinBtn).Click;
end;
function TCustomUpDown.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
Result := inherited DoMouseWheelUp(Shift, MousePos);
if not Result and not FUseWS then
TCustomSpeedButton(FMaxBtn).Click;
end;
function TCustomUpDown.DoMouseWheelLeft(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
Result := inherited DoMouseWheelLeft(Shift, MousePos);
if not Result then
if (Orientation=udHorizontal) and not FUseWS then
TCustomSpeedButton(FMinBtn).Click;
end;
function TCustomUpDown.DoMouseWheelRight(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
Result := inherited DoMouseWheelRight(Shift, MousePos);
if not Result then
if (Orientation=udHorizontal) and not FUseWS then
TCustomSpeedButton(FMaxBtn).Click;
end;
procedure TCustomUpDown.MouseEnter;
begin
if CheckMouseEntering then
begin
inherited MouseEnter;
FMouseInsideComp := True;
end;
end;
procedure TCustomUpDown.MouseLeave;
begin
if CheckMouseLeaving then
begin
inherited MouseLeave;
FMouseInsideComp := False;
end;
end;
procedure TCustomUpDown.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
begin
inherited;
UpdateOrientation;
end;
procedure TCustomUpDown.SetEnabled(Value: Boolean);
begin
if not FUseWS then
begin
FMinBtn.Enabled := Value;
FMaxBtn.Enabled := Value;
end;
inherited SetEnabled(Value);
end;
class function TCustomUpDown.GetControlClassDefaultSize: TSize;
begin
Result.CX := 17;
Result.CY := 31;
end;
procedure TCustomUpDown.CalculatePreferredSize(var PreferredWidth,
PreferredHeight: integer; WithThemeSpace: Boolean);
begin
case Orientation of
udHorizontal:
begin
PreferredWidth:=31;
PreferredHeight:=17;
end;
udVertical:
begin
PreferredWidth:=17;
PreferredHeight:=31;
end;
end;
end;
procedure TCustomUpDown.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FAssociate) then
SetAssociate(nil);
end;
function TCustomUpDown.CheckMouseEntering: Boolean;
// Mouse in entering this control.
begin
//debugln(['TCustomUpDown.CheckMouseEntering: FMouseInsideComp=',FMouseInsideComp]);
Result := not FMouseInsideComp;
end;
function TCustomUpDown.CheckMouseLeaving: Boolean;
// Mouse in leaving this control.
var
P: TPoint;
begin
GetCursorPos(P);
P:=ScreenToClient(P);
Result := not PtInRect(ClientRect, P);
//debugln(['TCustomUpDown.CheckMouseLeaving: PtInRect(ClientRect, P)=',PtInRect(ClientRect, P)]);
end;
function TCustomUpDown.GetPosition: SmallInt;
var
av,I : Integer;
str : string;
InvalidNumber : Boolean;
begin
If Associate <> nil then begin
str := Trim(Associate.Caption);
str := StringReplace(str, DefaultFormatSettings.ThousandSeparator, '', [rfReplaceAll]);
if not TryStrToInt(str, AV) then
Exit(FPosition);
//this will also correct for AV > High(SmallInt) or AV < Low(SMallInt)
If AV > FMax then
AV := FMax;
If AV < FMin then
AV := FMin;
Position := AV;
end;
Result := FPosition;
end;
function TCustomUpDown.GetFlat: Boolean;
begin
if FUseWS then
Result := false
else if FMinBtn<>nil then
Result := (FMinBtn as TSpeedButton).Flat
else
Result := False;
end;
procedure TCustomUpDown.SetMin(Value: SmallInt);
begin
if Value <> FMin then
begin
FMin := Value;
If FPosition < FMin then
Position := FMin;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetMinPosition(Self, FMin);
end;
end;
procedure TCustomUpDown.SetMinRepeatInterval(AValue: Byte);
begin
if FMinRepeatInterval = AValue then Exit;
FMinRepeatInterval := AValue;
if FMinRepeatInterval < 25 then FMinRepeatInterval := 25;
end;
procedure TCustomUpDown.SetMax(Value: SmallInt);
begin
if Value <> FMax then
begin
FMax := Value;
If FPosition > FMax then
Position := FMax;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetMaxPosition(Self, FMax);
end;
end;
procedure TCustomUpDown.SetIncrement(Value: Integer);
begin
if Value <> FIncrement then begin
FIncrement := Value;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetIncrement(Self, FIncrement);
end;
end;
procedure TCustomUpDown.SetPosition(Value: SmallInt);
begin
if FPosition = Value then exit;
FPosition := Value;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetPosition(Self, FPosition);
UpdateUpDownPositionText;
end;
procedure TCustomUpDown.SetOrientation(Value: TUDOrientation);
begin
if FOrientation = Value then exit;
FOrientation := Value;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetOrientation(Self, FOrientation);
UpdateOrientation;
end;
procedure TCustomUpDown.SetAlignButton(Value: TUDAlignButton);
begin
if FAlignButton = Value then exit;
FAlignButton := Value;
UpdateAlignButtonPos;
end;
procedure TCustomUpDown.SetArrowKeys(Value: Boolean);
begin
if Value <> FArrowKeys then begin
FArrowKeys := Value;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetUseArrowKeys(Self, Value);
end;
end;
procedure TCustomUpDown.SetThousands(Value: Boolean);
begin
if Value <> FThousands then
FThousands := Value;
end;
procedure TCustomUpDown.SetFlat(Value: Boolean);
begin
if FUseWS then Exit; // todo: not supported by WS yet
if Flat = Value then Exit;
(FMinBtn as TSpeedButton).Flat := Value;
(FMaxBtn as TSpeedButton).Flat := Value;
end;
procedure TCustomUpDown.SetWrap(Value: Boolean);
begin
if Value <> FWrap then
FWrap := Value;
if FUseWS then
TWSCustomUpDownClass(WidgetSetClass).SetWrap(Self, FWrap);
end;
// included by comctrls.pp