📄 tflattabcontrolunit.pas
字号:
unit TFlatTabControlUnit;
interface
{$I Version.inc}
uses
Windows, Messages, Classes, Controls, Forms, Graphics, StdCtrls, SysUtils, FlatUtilitys;
type
TFlatTabControl = class(TCustomControl)
private
FUseAdvColors: Boolean;
FAdvColorBorder: TAdvColors;
FTabPosition: TFlatTabPosition;
FTabs: TStrings;
FTabsRect: TList;
FTabHeight: Integer;
FTabSpacing: Integer;
FActiveTab: Integer;
FUnselectedColor: TColor;
FBorderColor: TColor;
FOnTabChanged: TNotifyEvent;
procedure SetTabs (Value: TStrings);
procedure SetTabPosition (Value: TFlatTabPosition);
procedure SetTabHeight (Value: Integer);
procedure SetTabSpacing (Value: Integer);
procedure SetActiveTab (Value: Integer);
procedure SetColors (Index: Integer; Value: TColor);
procedure SetAdvColors (Index: Integer; Value: TAdvColors);
procedure SetUseAdvColors (Value: Boolean);
procedure SetTabRect;
procedure CMDialogChar (var Message: TCMDialogChar); message CM_DIALOGCHAR;
procedure CMEnabledChanged (var Message: TMessage); message CM_ENABLEDCHANGED;
procedure WMSize (var Message: TWMSize); message WM_SIZE;
procedure CMSysColorChange (var Message: TMessage); message CM_SYSCOLORCHANGE;
procedure CMParentColorChanged (var Message: TWMNoParams); message CM_PARENTCOLORCHANGED;
procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST;
protected
procedure CalcAdvColors;
procedure MouseDown (Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure Loaded; override;
procedure Paint; override;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property ColorBorder: TColor index 0 read FBorderColor write SetColors default $008396A0;
property ColorUnselectedTab: TColor index 1 read FUnselectedColor write SetColors default $00996633;
property AdvColorBorder: TAdvColors index 0 read FAdvColorBorder write SetAdvColors default 50;
property UseAdvColors: Boolean read FUseAdvColors write SetUseAdvColors default false;
property Tabs: TStrings read FTabs write SetTabs;
property TabHeight: Integer read FTabHeight write SetTabHeight default 16;
property TabSpacing: Integer read FTabSpacing write SetTabSpacing default 4;
property TabPosition: TFlatTabPosition read FTabPosition write SetTabPosition default tpTop;
property ActiveTab: Integer read FActiveTab write SetActiveTab default 0;
property Font;
property Color;
property ParentColor;
property Enabled;
property Visible;
property Cursor;
property ParentShowHint;
property ParentFont;
property ShowHint;
property OnEnter;
property OnExit;
property OnMouseMove;
property OnMouseDown;
property OnMouseUp;
property OnTabChanged: TNotifyEvent read FOnTabChanged write FOnTabChanged;
{$IFDEF D4CB4}
property Anchors;
property BiDiMode;
property Constraints;
property DragKind;
property ParentBiDiMode;
property OnEndDock;
property OnStartDock;
{$ENDIF}
end;
implementation
procedure TFlatTabControl.CMDesignHitTest(var Message: TCMDesignHitTest);
begin
case FTabPosition of
tpTop:
if PtInRect(Rect(ClientRect.Left, ClientRect.Top, ClientRect.Right, ClientRect.Top + FTabHeight + 1), Point(message.XPos, message.YPos)) then
Message.Result := 1
else
Message.Result := 0;
tpBottom:
if PtInRect(Rect(ClientRect.Left, ClientRect.Bottom - FTabHeight, ClientRect.Right, ClientRect.Bottom), Point(message.XPos, message.YPos)) then
Message.Result := 1
else
Message.Result := 0;
end;
end;
constructor TFlatTabControl.Create (AOwner: TComponent);
begin
inherited;
ControlStyle := ControlStyle + [csAcceptsControls, csOpaque];
SetBounds(Left, Top, 289, 193);
FTabs := TStringList.Create;
FTabsRect := TList.Create;
FTabHeight := 16;
FTabSpacing := 4;
FTabPosition := tpTop;
FActiveTab := 0;
Color := $00E1EAEB;
FBorderColor := $008396A0;
FUnselectedColor := $00996633;
ParentColor := True;
ParentFont := True;
FUseAdvColors := false;
FAdvColorBorder := 50;
end;
destructor TFlatTabControl.Destroy;
begin
FTabs.Free;
FTabsRect.Free;
inherited;
end;
procedure TFlatTabControl.CalcAdvColors;
begin
if FUseAdvColors then
begin
FBorderColor := CalcAdvancedColor(Color, FBorderColor, FAdvColorBorder, darken);
end;
end;
procedure TFlatTabControl.SetAdvColors (Index: Integer; Value: TAdvColors);
begin
case Index of
0: FAdvColorBorder := Value;
end;
CalcAdvColors;
Invalidate;
end;
procedure TFlatTabControl.SetUseAdvColors (Value: Boolean);
begin
if Value <> FUseAdvColors then
begin
FUseAdvColors := Value;
ParentColor := Value;
CalcAdvColors;
Invalidate;
end;
end;
procedure TFlatTabControl.CMSysColorChange (var Message: TMessage);
begin
if FUseAdvColors then
begin
ParentColor := True;
CalcAdvColors;
end;
Invalidate;
end;
procedure TFlatTabControl.CMParentColorChanged (var Message: TWMNoParams);
begin
inherited;
if FUseAdvColors then
begin
ParentColor := True;
CalcAdvColors;
end;
Invalidate;
end;
procedure TFlatTabControl.Loaded;
begin
inherited;
SetTabRect;
Invalidate;
end;
procedure TFlatTabControl.WMSize (var Message: TWMSize);
begin
inherited;
SetTabRect;
Invalidate;
end;
procedure TFlatTabControl.CMEnabledChanged (var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TFlatTabControl.SetTabPosition (Value: TFlatTabPosition);
var
I: Integer;
begin
if Value <> FTabPosition then
begin
for I := 0 to ControlCount - 1 do // reposition client-controls
begin
if Value = tpTop then
Controls[I].Top := Controls[I].Top + TabHeight
else
Controls[I].Top := Controls[I].Top - TabHeight;
end;
FTabPosition := Value;
SetTabRect;
Invalidate;
end;
end;
procedure TFlatTabControl.SetActiveTab (Value: Integer);
begin
if FTabs <> nil then
begin
if Value > (FTabs.Count - 1) then
Value := FTabs.Count - 1
else
if Value < 0 then
Value := 0;
FActiveTab := Value;
if Assigned(FOnTabChanged) then
FOnTabChanged(Self);
Invalidate;
end
else
FActiveTab := 0;
end;
procedure TFlatTabControl.SetColors (Index: Integer; Value: TColor);
begin
case Index of
0: FBorderColor := Value;
1: FUnselectedColor := Value;
end;
Invalidate;
end;
procedure TFlatTabControl.SetTabs (Value: TStrings);
var
counter: Integer;
begin
FTabs.Assign(Value);
if FTabs.Count = 0 then // no tabs? then active tab = 0
FActiveTab := 0
else
begin
if (FTabs.Count - 1) < FActiveTab then // if activeTab > last-tab the activeTab = last-tab
FActiveTab := FTabs.Count - 1;
for counter := 0 to FTabs.Count - 1 do
FTabs[counter] := Trim(FTabs[counter]); // delete all spaces at left and right
end;
SetTabRect;
Invalidate;
end;
procedure TFlatTabControl.SetTabHeight (Value: Integer);
begin
if Value < 0 then Value := 0; // TabHeigh can't negative
FTabHeight := Value;
SetTabRect;
Invalidate;
end;
procedure TFlatTabControl.SetTabSpacing (Value: Integer);
begin
if Value < 1 then Value := 1; // minimal tabspacing = 1 dot
FTabSpacing := Value;
SetTabRect;
Invalidate;
end;
procedure TFlatTabControl.SetTabRect;
var
TabCount: Integer;
TabRect: ^TRect;
position: TPoint;
CaptionTextWidth: Integer;
CaptionTextString: string;
begin
// set the font and clear the tab-rect-list
canvas.font := self.font;
FTabsRect.Clear;
// set left/top position for the the first tab
case FTabPosition of
tpTop:
position := Point(ClientRect.left, ClientRect.top);
tpBottom:
position := Point(ClientRect.left, ClientRect.bottom - FTabHeight);
end;
for TabCount := 0 to (FTabs.Count - 1) do
begin
New(TabRect); // create a new Tab-Rect
if Pos('&', FTabs[TabCount]) <> 0 then // if & in an caption
begin
CaptionTextString := FTabs[TabCount]; // read the caption text
Delete(CaptionTextString, Pos('&', FTabs[TabCount]), 1); // delete the &
CaptionTextWidth := canvas.TextWidth(CaptionTextString); // calc the caption-width withou the &
end
else // else calc the caption-width
CaptionTextWidth := canvas.TextWidth(FTabs[TabCount]);
case FTabPosition of // set the rect
tpTop:
TabRect^ := Rect(position.x, position.y, position.x + CaptionTextWidth + 20, FTabHeight);
tpBottom:
TabRect^ := Rect(position.x, position.y, position.x + CaptionTextWidth + 20, position.y + FTabHeight);
end;
position := Point(position.x + CaptionTextWidth + 20 + FTabSpacing, position.y); // set left/top position for next rect
FTabsRect.Add(TabRect); // add the tab-rect to the tab-rect-list
end;
end;
procedure TFlatTabControl.CMDialogChar (var Message: TCMDialogChar);
var
currentTab: Integer;
begin
with Message do
begin
if FTabs.Count > 0 then
begin
for currentTab := 0 to FTabs.Count - 1 do
begin
if IsAccel(CharCode, FTabs[currentTab]) then
begin
if (FActiveTab <> currentTab) then
begin
SetActiveTab(currentTab);
SetFocus;
end;
Result := 1;
break;
end;
end;
end
else
inherited;
end;
end;
procedure TFlatTabControl.Paint;
var
abc: TBitmap;
TabCount: Integer;
rect: ^TRect;
begin
abc := TBitmap.Create; // create memory-bitmap to draw flicker-free
try
abc.Height := ClientRect.Bottom;
abc.Width := ClientRect.Right;
abc.Canvas.Font := Self.Font;
// Clear Background
abc.canvas.Brush.Color := Self.Color;
abc.canvas.FillRect(ClientRect);
// Draw Border
if FTabs.Count = 0 then
begin
abc.canvas.Brush.Color := FBorderColor;
abc.canvas.FrameRect(ClientRect)
end
else
begin
abc.canvas.Pen.Color := FBorderColor;
rect := FTabsRect.Items[FActiveTab];
if ClientRect.left <> rect^.left then // if Active Tab not first tab then __|Tab|___
begin
case FTabPosition of
tpTop:
begin
abc.Canvas.Polyline([Point(ClientRect.left, ClientRect.top + FTabHeight), Point(rect^.Left, ClientRect.top + FTabHeight)]);
abc.Canvas.Polyline([Point(rect^.Right-1, ClientRect.top + FTabHeight), Point(ClientRect.right, ClientRect.top + FTabHeight)]);
end;
tpBottom:
begin
abc.Canvas.Polyline([Point(ClientRect.left, ClientRect.bottom - FTabHeight - 1), Point(rect^.Left, ClientRect.bottom - FTabHeight - 1)]);
abc.Canvas.Polyline([Point(rect^.Right-1, ClientRect.bottom - FTabHeight - 1), Point(ClientRect.right, ClientRect.bottom - FTabHeight - 1)]);
end;
end;
end
else // else |Tab|___
case FTabPosition of
tpTop:
abc.Canvas.Polyline([Point(rect^.Right-1, ClientRect.top + FTabHeight), Point(ClientRect.right, ClientRect.top + FTabHeight)]);
tpBottom:
abc.Canvas.Polyline([Point(rect^.Right-1, ClientRect.bottom - FTabHeight - 1), Point(ClientRect.right, ClientRect.bottom - FTabHeight - 1)]);
end;
// border of the control
case FTabPosition of
tpTop:
abc.Canvas.Polyline([Point(ClientRect.left, ClientRect.top + FTabHeight), Point(ClientRect.left, ClientRect.bottom - 1), Point(ClientRect.right - 1, ClientRect.bottom - 1), Point(ClientRect.right - 1, ClientRect.top + FTabHeight)]);
tpBottom:
abc.Canvas.Polyline([Point(ClientRect.left, ClientRect.bottom - FTabHeight - 1), Point(ClientRect.left, ClientRect.top), Point(ClientRect.right - 1, ClientRect.top), Point(ClientRect.right - 1, ClientRect.bottom - FTabHeight - 1)]);
end;
end;
// Draw Tabs
for TabCount := 0 to FTabs.Count - 1 do
begin
rect := FTabsRect.Items[TabCount];
abc.canvas.brush.style := bsclear;
abc.canvas.pen.color := clBlack;
if TabCount = FActiveTab then // if Active Tab not first tab then draw border |^^^|
begin
abc.canvas.font.color := self.font.color;
abc.canvas.pen.color := FBorderColor;
case FTabPosition of
tpTop:
abc.Canvas.Polyline([Point(rect^.Left, rect^.Bottom), Point(rect^.Left, rect^.Top), Point(rect^.Right-1, rect^.Top), Point(rect^.Right-1, rect^.Bottom)]);
tpBottom:
abc.Canvas.Polyline([Point(rect^.Left, rect^.top - 1), Point(rect^.Left, rect^.bottom - 1), Point(rect^.Right-1, rect^.bottom - 1), Point(rect^.Right-1, rect^.top - 1)]);
end;
end
else // else only fill the tab
begin
abc.canvas.font.color := color;
abc.canvas.brush.color := FUnselectedColor;
abc.Canvas.FillRect(rect^);
end;
if (TabCount = FActiveTab) and not Enabled then
begin
abc.Canvas.Font.Color := FUnselectedColor;
DrawText(abc.canvas.Handle, PChar(FTabs[TabCount]), Length(FTabs[TabCount]), rect^, DT_CENTER or DT_VCENTER or DT_SINGLELINE)
end
else
DrawText(abc.canvas.Handle, PChar(FTabs[TabCount]), Length(FTabs[TabCount]), rect^, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
end;
canvas.CopyRect(ClientRect, abc.canvas, ClientRect); // Copy bitmap to screen
finally
abc.free; // delete the bitmap
end;
end;
procedure TFlatTabControl.MouseDown (Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
cursorPos: TPoint;
currentTab: Integer;
currentRect: ^TRect;
begin
GetCursorPos(cursorPos);
cursorPos := ScreenToClient(cursorPos);
if FTabs.Count > 0 then
begin
for currentTab := 0 to FTabs.Count - 1 do
begin
currentRect := FTabsRect.Items[currentTab];
if PtInRect(currentRect^, cursorPos) then
if (FActiveTab <> currentTab) then // only change when new tab selected
begin
SetActiveTab(currentTab);
SetFocus;
end;
end;
end;
inherited;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -