lazarus-ccr/components/jvcllaz/run/JvPageComps/jvtabbar.pas
2019-05-26 22:58:36 +00:00

2758 lines
77 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvTabBar.pas, released on 2004-12-23.
The Initial Developer of the Original Code is Andreas Hausladen <Andreas dott Hausladen att gmx dott de>
Portions created by Andreas Hausladen are Copyright (C) 2004 Andreas Hausladen.
All Rights Reserved.
Contributor(s):
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.delphi-jedi.org
Known Issues:
-----------------------------------------------------------------------------}
// $Id$
unit JvTabBar;
{$MODE objfpc}{$H+}
{.$DEFINE JVCLThemesEnabled}
interface
uses
LCLIntf, LCLType, LMessages, LCLVersion, Types,
Graphics, Controls, Forms, ImgList, Menus, Buttons,
ExtCtrls, SysUtils, Classes, Contnrs, Themes,
JvPageList;
type
TJvCustomTabBar = class;
TJvTabBarItem = class;
TJvTabBarOrientation = (toTop, toBottom);
TJvTabBarScrollButtonKind = (sbScrollLeft, sbScrollRight);
TJvTabBarScrollButtonState = (sbsHidden, sbsNormal, sbsHot, sbsPressed, sbsDisabled);
TJvGetModifiedEvent = procedure(Sender: TJvTabBarItem; var Modified: Boolean) of object;
TJvGetEnabledEvent = procedure(Sender: TJvTabBarItem; var Enabled: Boolean) of object;
(*
IPageList = interface
['{6BB90183-CFB1-4431-9CFD-E9A032E0C94C}']
function CanChange(AIndex: Integer): Boolean;
procedure SetActivePageIndex(AIndex: Integer);
function GetPageCount: Integer;
function GetPageCaption(AIndex: Integer): string;
procedure AddPage(const ACaption: string);
procedure DeletePage(Index: Integer);
procedure MovePage(CurIndex, NewIndex: Integer);
procedure PageCaptionChanged(Index: Integer; const NewCaption: string);
end;
*)
TJvTabBarItem = class(TCollectionItem)
private
FLeft: Integer; // used for calculating DisplayRect
FImageIndex: TImageIndex;
FEnabled: Boolean;
FVisible: Boolean;
FTag: Integer;
FData: TObject;
FHint: TCaption;
FName: string;
FCaption: TCaption;
FImages: TCustomImageList;
FModified: Boolean;
FPopupMenu: TPopupMenu;
FOnGetEnabled: TJvGetEnabledEvent;
FOnGetModified: TJvGetModifiedEvent;
FShowHint: Boolean;
FAutoDeleteDatas: TObjectList;
function GetEnabled: Boolean;
function GetModified: Boolean;
procedure SetPopupMenu(const Value: TPopupMenu);
function GetClosing: Boolean;
procedure SetModified(const Value: Boolean);
procedure SetCaption(const Value: TCaption);
procedure SetSelected(const Value: Boolean);
procedure SetEnabled(const Value: Boolean);
procedure SetImageIndex(const Value: TImageIndex);
procedure SetName(const Value: string);
procedure SetVisible(const Value: Boolean);
function GetTabBar: TJvCustomTabBar;
function GetSelected: Boolean;
function GetDisplayRect: TRect;
function GetHot: Boolean;
protected
procedure Changed; virtual;
procedure SetIndex(Value: Integer); override;
procedure Notification(Component: TComponent; Operation: TOperation); virtual;
property Name: string read FName write SetName;
public
constructor Create(ACollection: Classes.TCollection); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function GetImages: TCustomImageList;
function CanSelect: Boolean;
function GetNextVisible: TJvTabBarItem;
function GetPreviousVisible: TJvTabBarItem;
procedure MakeVisible;
function AutoDeleteData: TObjectList;
property Data: TObject read FData write FData;
property TabBar: TJvCustomTabBar read GetTabBar;
property DisplayRect: TRect read GetDisplayRect;
property Hot: Boolean read GetHot;
property Closing: Boolean read GetClosing;
published
property Caption: TCaption read FCaption write SetCaption;
property Selected: Boolean read GetSelected write SetSelected default False;
property Enabled: Boolean read GetEnabled write SetEnabled default True;
property Modified: Boolean read GetModified write SetModified default False;
property Hint: TCaption read FHint write FHint;
property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
property Tag: Integer read FTag write FTag default 0;
property Visible: Boolean read FVisible write SetVisible default True;
property PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
property ShowHint: Boolean read FShowHint write FShowHint default True;
property OnGetModified: TJvGetModifiedEvent read FOnGetModified write FOnGetModified;
property OnGetEnabled: TJvGetEnabledEvent read FOnGetEnabled write FOnGetEnabled;
end;
TJvTabBarItems = class(TOwnedCollection)
private
function GetTabBar: TJvCustomTabBar;
function GetItem(Index: Integer): TJvTabBarItem;
procedure SetItem(Index: Integer; const Value: TJvTabBarItem);
protected
function Find(const AName: string): TJvTabBarItem;
procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); override;
public
function IndexOf(Item: TJvTabBarItem): Integer;
procedure EndUpdate; override;
property Items[Index: Integer]: TJvTabBarItem read GetItem write SetItem; default;
property TabBar: TJvCustomTabBar read GetTabBar;
end;
TJvTabBarPainterOptionType = (poPaintsHotTab, poBottomScrollButtons);
TJvTabBarPainterOptions = set of TJvTabBarPainterOptionType;
TJvTabBarPainter = class(TComponent)
private
FOnChangeList: TList;
protected
procedure AutoSize; virtual;
procedure Changed; virtual;
procedure DrawBackground(Canvas: TCanvas; TabBar: TJvCustomTabBar; R: TRect); virtual; abstract;
procedure DrawDivider(Canvas: TCanvas; LeftTab: TJvTabBarItem; R: TRect); virtual; abstract;
procedure DrawMoveDivider(Canvas: TCanvas; Tab: TJvTabBarItem; MoveLeft: Boolean); virtual; abstract;
procedure DrawScrollButton(Canvas: TCanvas; TabBar: TJvCustomTabBar; Button: TJvTabBarScrollButtonKind;
State: TJvTabBarScrollButtonState; R: TRect); virtual;
procedure DrawTab(Canvas: TCanvas; Tab: TJvTabBarItem; R: TRect); virtual; abstract;
function GetCloseRect(Canvas: TCanvas; Tab: TJvTabBarItem; R: TRect): TRect; virtual; abstract;
function GetDividerWidth(Canvas: TCanvas; LeftTab: TJvTabBarItem): Integer; virtual; abstract;
function GetPixelsPerInch: Integer; virtual; abstract;
function GetRealImageSize(ATab: TJvTabBarItem): TSize;
procedure GetScrollButtons(TabBar: TJvCustomTabBar; var LeftButton, RightButton: TRect); {virtual; reserved for future use }
function GetTabBar(ATab: TJvTabBarItem): TJvCustomTabBar;
function GetTabSize(Canvas: TCanvas; Tab: TJvTabBarItem): TSize; virtual; abstract;
function Options: TJvTabBarPainterOptions; virtual; abstract;
function Scale96(AValue: Integer): Integer;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
TJvModernTabBarPainter = class(TJvTabBarPainter)
private
FFont: TFont;
FDisabledFont: TFont;
FSelectedFont: TFont;
FColor: TColor;
FTabColor: TColor;
FControlDivideColor: TColor;
FBorderColor: TColor;
FModifiedCrossColor: TColor;
FCloseRectColor: TColor;
FCloseRectColorDisabled: TColor;
FCloseCrossColorDisabled: TColor;
FCloseCrossColorSelected: TColor;
FCloseCrossColor: TColor;
FCloseColor: TColor;
FCloseColorSelected: TColor;
FDividerColor: TColor;
FMoveDividerColor: TColor;
FTabHeight: Integer;
FTabWidth: Integer;
procedure SetCloseRectColorDisabled(const Value: TColor);
procedure SetCloseColor(const Value: TColor);
procedure SetCloseColorSelected(const Value: TColor);
procedure SetCloseCrossColor(const Value: TColor);
procedure SetCloseCrossColorDisabled(const Value: TColor);
procedure SetCloseRectColor(const Value: TColor);
procedure SetFont(const Value: TFont);
procedure SetDisabledFont(const Value: TFont);
procedure SetSelectedFont(const Value: TFont);
procedure SetModifiedCrossColor(const Value: TColor);
procedure SetBorderColor(const Value: TColor);
procedure SetControlDivideColor(const Value: TColor);
procedure SetTabColor(const Value: TColor);
procedure SetColor(const Value: TColor);
procedure FontChanged(Sender: TObject);
procedure SetDividerColor(const Value: TColor);
procedure SetCloseCrossColorSelected(const Value: TColor);
procedure SetTabHeight(Value: Integer);
procedure SetTabWidth(Value: Integer);
protected
procedure AutoSize; override;
procedure DrawBackground(Canvas: TCanvas; TabBar: TJvCustomTabBar; R: TRect); override;
procedure DrawDivider(Canvas: TCanvas; LeftTab: TJvTabBarItem; R: TRect); override;
procedure DrawMoveDivider(Canvas: TCanvas; Tab: TJvTabBarItem; MoveLeft: Boolean); override;
procedure DrawTab(Canvas: TCanvas; Tab: TJvTabBarItem; ATabRect: TRect); override;
function GetCloseRect(Canvas: TCanvas; Tab: TJvTabBarItem; ATabRect: TRect): TRect; override;
function GetDividerWidth(Canvas: TCanvas; LeftTab: TJvTabBarItem): Integer; override;
function GetPixelsPerInch: Integer; override;
function GetTabSize(Canvas: TCanvas; Tab: TJvTabBarItem): TSize; override;
function Options: TJvTabBarPainterOptions; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property TabColor: TColor read FTabColor write SetTabColor default clBtnFace;
property Color: TColor read FColor write SetColor default clWindow;
property BorderColor: TColor read FBorderColor write SetBorderColor default clSilver;
property ControlDivideColor: TColor read FControlDivideColor write SetControlDivideColor default clBlack;
property ModifiedCrossColor: TColor read FModifiedCrossColor write SetModifiedCrossColor default clRed;
property CloseColorSelected: TColor read FCloseColorSelected write SetCloseColorSelected default $F4F4F4;
property CloseColor: TColor read FCloseColor write SetCloseColor default clWhite;
property CloseCrossColorSelected: TColor read FCloseCrossColorSelected write SetCloseCrossColorSelected default clBlack;
property CloseCrossColor: TColor read FCloseCrossColor write SetCloseCrossColor default $5D5D5D;
property CloseCrossColorDisabled: TColor read FCloseCrossColorDisabled write SetCloseCrossColorDisabled default $ADADAD;
property CloseRectColor: TColor read FCloseRectColor write SetCloseRectColor default $868686;
property CloseRectColorDisabled: TColor read FCloseRectColorDisabled write SetCloseRectColorDisabled default $D6D6D6;
property DividerColor: TColor read FDividerColor write SetDividerColor default $99A8AC;
property MoveDividerColor: TColor read FMoveDividerColor write FMoveDividerColor default clBlack;
property TabHeight: Integer read FTabHeight write SetTabHeight default 0;
property TabWidth: Integer read FTabWidth write SetTabWidth default 0;
property Font: TFont read FFont write SetFont;
property DisabledFont: TFont read FDisabledFont write SetDisabledFont;
property SelectedFont: TFont read FSelectedFont write SetSelectedFont;
end;
TJvTabBarModernPainter = TJvModernTabBarPainter; // TJvModernTabBarPainter should have been named TJvTabBarModernPainter
TJvTabBarItemEvent = procedure(Sender: TObject; Item: TJvTabBarItem) of object;
TJvTabBarSelectingEvent = procedure(Sender: TObject; Item: TJvTabBarItem; var AllowSelect: Boolean) of object;
TJvTabBarClosingEvent = procedure(Sender: TObject; Item: TJvTabBarItem; var AllowClose: Boolean) of object;
TJvTabBarCloseQueryEvent = procedure(Sender: TObject; Item: TJvTabBarItem; var CanClose: Boolean) of object;
TJvTabBarScrollButtonClickEvent = procedure(Sender: TObject; Button: TJvTabBarScrollButtonKind) of object;
TJvTabBarScrollButtonInfo = record
State: TJvTabBarScrollButtonState;
Rect: TRect;
ExState: Boolean;
end;
TJvCustomTabBar = class(TCustomControl)
private
FTabs: TJvTabBarItems;
FPainter: TJvTabBarPainter;
FDefaultPainter: TJvTabBarPainter;
FChangeLink: TChangeLink;
FCloseButton: Boolean;
FRightClickSelect: Boolean;
FImages: TCustomImageList;
FImagesWidth: Integer;
FHotTracking: Boolean;
FHotTab: TJvTabBarItem;
FSelectedTab: TJvTabBarItem;
FClosingTab: TJvTabBarItem;
FLastInsertTab: TJvTabBarItem;
FMouseDownClosingTab: TJvTabBarItem;
FMargin: Integer;
FAutoFreeClosed: Boolean;
FAllowUnselected: Boolean;
FSelectBeforeClose: Boolean;
FPageList: TCustomControl;
FOnTabClosing: TJvTabBarClosingEvent;
FOnTabSelected: TJvTabBarItemEvent;
FOnTabSelecting: TJvTabBarSelectingEvent;
FOnTabCloseQuery: TJvTabBarCloseQueryEvent;
FOnTabClosed: TJvTabBarItemEvent;
FOnTabMoved: TJvTabBarItemEvent;
FOnChange: TNotifyEvent;
// scrolling
FLeftIndex: Integer;
FLastTabRight: Integer;
FRequiredWidth: Integer;
FBarWidth: Integer;
FBtnLeftScroll: TJvTabBarScrollButtonInfo;
FBtnRightScroll: TJvTabBarScrollButtonInfo;
FScrollButtonBackground: TBitmap;
FHint: TCaption;
FFlatScrollButtons: Boolean;
FAllowTabMoving: Boolean;
FOrientation: TJvTabBarOrientation;
FOnScrollButtonClick: TJvTabBarScrollButtonClickEvent;
FPageListTabLink: Boolean;
FRepeatTimer: TTimer;
FScrollRepeatedClicked: Boolean;
FOnLeftTabChange: TNotifyEvent;
function GetHeight: Integer;
function GetLeftTab: TJvTabBarItem;
procedure SetLeftTab(Value: TJvTabBarItem);
procedure SetSelectedTab(Value: TJvTabBarItem);
procedure SetTabs(Value: TJvTabBarItems);
procedure SetPainter(Value: TJvTabBarPainter);
procedure SetImages(Value: TCustomImageList);
procedure SetCloseButton(Value: Boolean);
procedure SetMargin(Value: Integer);
procedure SetHotTab(Tab: TJvTabBarItem);
procedure SetClosingTab(Tab: TJvTabBarItem);
procedure UpdateScrollButtons;
function FindSelectableTab(Tab: TJvTabBarItem): TJvTabBarItem;
procedure SetHint(const Value: TCaption);
procedure SetFlatScrollButtons(const Value: Boolean);
procedure SetPageList(const Value: TCustomControl);
procedure SetOrientation(const Value: TJvTabBarOrientation);
procedure TimerExpired(Sender: TObject);
procedure SetHeight(AValue: Integer);
{$IF LCL_FullVersion >= 1090000}
private
procedure SetImagesWidth(const AValue: Integer);
protected
property ImagesWidth: Integer read FImagesWidth write SetImagesWidth default 0;
{$ENDIF}
protected
procedure CalcTabsRects;
procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
WithThemeSpace: Boolean); override;
procedure DrawScrollBarGlyph(ACanvas: TCanvas; X, Y: Integer; ALeft, Disabled: Boolean);
procedure Paint; override;
procedure PaintScrollButtons;
procedure PaintTab(ACanvas: TCanvas; Tab: TJvTabBarItem); virtual;
procedure Resize; override;
class function GetControlClassDefaultSize: TSize;
function GetTabHeight(Tab: TJvTabBarItem): Integer;
function GetTabWidth(Tab: TJvTabBarItem): Integer;
function CurrentPainter: TJvTabBarPainter;
procedure Notification(Component: TComponent; Operation: TOperation); override;
function TabClosing(Tab: TJvTabBarItem): Boolean; virtual;
function TabCloseQuery(Tab: TJvTabBarItem): Boolean; virtual;
procedure TabClosed(Tab: TJvTabBarItem); virtual;
function TabSelecting(Tab: TJvTabBarItem): Boolean; virtual;
procedure TabSelected(Tab: TJvTabBarItem); virtual;
procedure TabMoved(Tab: TJvTabBarItem); virtual;
procedure Changed; virtual;
procedure ImagesChanged(Sender: TObject); virtual;
procedure ScrollButtonClick(Button: TJvTabBarScrollButtonKind); virtual;
procedure LeftTabChanged; virtual;
procedure DragOver(Source: TObject; X: Integer; Y: Integer;
State: TDragState; var Accept: Boolean); override;
procedure DragCanceled; override;
function ScrollButtonsMouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer): Boolean; virtual;
function ScrollButtonsMouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer): Boolean; virtual;
function ScrollButtonsMouseMove(Shift: TShiftState; X: Integer; Y: Integer): Boolean; virtual;
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
procedure CMMouseLeave(var Msg: TLMessage); message CM_MOUSELEAVE;
procedure WMEraseBkgnd(var Msg: TLMEraseBkgnd); message LM_ERASEBKGND;
procedure Loaded; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function AddTab(const ACaption: string): TJvTabBarItem;
function FindTab(const ACaption: string): TJvTabBarItem; // returns the first tab with the given Caption
function TabAt(X, Y: Integer): TJvTabBarItem;
function MakeVisible(Tab: TJvTabBarItem): Boolean;
function FindData(Data: TObject): TJvTabBarItem;
function CloseTab(ATab: TJvTabBarItem): Boolean;
procedure DragDrop(Source: TObject; X: Integer; Y: Integer); override;
property PageListTabLink: Boolean read FPageListTabLink write FPageListTabLink default False; // if true the PageList's Pages[] are kept in sync with the Tabs
property PageList: TCustomControl read FPageList write SetPageList;
property Painter: TJvTabBarPainter read FPainter write SetPainter;
property Images: TCustomImageList read FImages write SetImages;
property Tabs: TJvTabBarItems read FTabs write SetTabs;
// Status
property SelectedTab: TJvTabBarItem read FSelectedTab write SetSelectedTab;
property LeftTab: TJvTabBarItem read GetLeftTab write SetLeftTab;
property HotTab: TJvTabBarItem read FHotTab;
property ClosingTab: TJvTabBarItem read FClosingTab;
// Options
property Orientation: TJvTabBarOrientation read FOrientation write SetOrientation default toTop;
property CloseButton: Boolean read FCloseButton write SetCloseButton default True;
property RightClickSelect: Boolean read FRightClickSelect write FRightClickSelect default True;
property HotTracking: Boolean read FHotTracking write FHotTracking default False;
property AutoFreeClosed: Boolean read FAutoFreeClosed write FAutoFreeClosed default True;
property AllowUnselected: Boolean read FAllowUnselected write FAllowUnselected default False;
property SelectBeforeClose: Boolean read FSelectBeforeClose write FSelectBeforeClose default False;
property Margin: Integer read FMargin write SetMargin default 6;
property FlatScrollButtons: Boolean read FFlatScrollButtons write SetFlatScrollButtons default True;
property Height read GetHeight write SetHeight;
property Hint: TCaption read FHint write SetHint;
property AllowTabMoving: Boolean read FAllowTabMoving write FAllowTabMoving default False;
// Events
{ With OnTabClosing you can prevent the close button [X] in the tab from shrinking.
If you want to ask the user you should use OnTabCloseQuery }
property OnTabClosing: TJvTabBarClosingEvent read FOnTabClosing write FOnTabClosing;
property OnTabCloseQuery: TJvTabBarCloseQueryEvent read FOnTabCloseQuery write FOnTabCloseQuery;
property OnTabClosed: TJvTabBarItemEvent read FOnTabClosed write FOnTabClosed;
property OnTabSelecting: TJvTabBarSelectingEvent read FOnTabSelecting write FOnTabSelecting;
property OnTabSelected: TJvTabBarItemEvent read FOnTabSelected write FOnTabSelected;
property OnTabMoved: TJvTabBarItemEvent read FOnTabMoved write FOnTabMoved;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnScrollButtonClick: TJvTabBarScrollButtonClickEvent read FOnScrollButtonClick write FOnScrollButtonClick;
property OnLeftTabChange: TNotifyEvent read FOnLeftTabChange write FOnLeftTabChange;
end;
TJvTabBar = class(TJvCustomTabBar)
published
property Align default alTop;
property AutoSize default true;
property BorderSpacing;
property Constraints;
property Cursor;
property PopupMenu;
property ShowHint default False;
property Height;
property Hint;
property Visible;
property Enabled;
property Orientation;
property CloseButton;
property RightClickSelect;
property HotTracking;
property AutoFreeClosed;
property AllowUnselected;
property SelectBeforeClose;
property Margin;
property FlatScrollButtons;
property AllowTabMoving;
property PageListTabLink;
property PageList;
property Painter;
property Images;
{$IF LCL_FullVersion >= 1090000}
property ImagesWidth;
{$ENDIF}
property Tabs;
property OnTabClosing;
property OnTabCloseQuery;
property OnTabClosed;
property OnTabSelecting;
property OnTabSelected;
property OnTabMoved;
property OnChange;
property OnLeftTabChange;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnContextPopup;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnStartDrag;
property OnEndDrag;
property OnStartDock;
property OnEndDock;
end;
implementation
uses
JvJVCLUtils;
const
WHEEL_DELTA = 120;
// Pixels at 96 ppi:
LEFT_MARGIN = 8; // Distance left edge to image or text
RIGHT_MARGIN = 8; // Distance right edge to close button or text
TEXT_MARGIN_LEFT = 8; // Distance image to text
TEXT_MARGIN_RIGHT = 8; // Distance text to close btn
TOP_MARGIN = 4; // Distance top edge to text/image/close btn (whichever is highest)
BOTTOM_MARGIN = 4; // Distance bottom edge to text/image/close btn (whichever is lowest)
CLOSE_BUTTON_SIZE = 12; // size of the close button box
CROSS_MARGIN = 8; // Margin of the "x" inside the close button
function DrawButtonFace(ACanvas: TCanvas; const ARect: TRect; AFlat: Boolean;
// BevelWidth: Integer; Style: TButtonStyle; IsRounded,
IsDown, IsFocused: Boolean): TRect;
var
R: TRect;
begin
R := ARect;
ACanvas.Pen.Style := psSolid;
if AFlat then begin
ACanvas.Brush.Color := clBtnFace;
ACanvas.Brush.Style := bsSolid;
ACanvas.FillRect(R);
if IsDown then begin
ACanvas.Pen.Color := clBlack;
ACanvas.Line(R.Left, R.Top, R.Left, R.Bottom-1); // left
ACanvas.Line(R.Left, R.Top, R.Right-1, R.Top); // top
ACanvas.Pen.Color := clBtnHighlight;
ACanvas.Line(R.Right-1, R.Top, R.Right-1, R.Bottom-1); // right
ACanvas.Line(R.Left, R.Bottom-1, R.Right-1, R.Bottom-1); // bottom
InflateRect(R, -1, -1);
ACanvas.Pen.Color := clBtnShadow;
ACanvas.Line(R.Left, R.Top, R.Left, R.Bottom-1); // left
ACanvas.Line(R.Left, R.Top, R.Right-1, R.Top); // top
end else begin
ACanvas.Pen.Color := clBlack;
ACanvas.Line(R.Right-1, R.Top, R.Right-1, R.Bottom-1); // right
ACanvas.Line(R.Left, R.Bottom-1, R.Right-1, R.Bottom-1); // bottom
dec(R.Right);
dec(R.Bottom);
ACanvas.Pen.Color := clBtnHighlight;
ACanvas.Line(R.Left, R.Top, R.Left, R.Bottom-1); // left
ACanvas.Line(R.Left, R.Top, R.Right-1, R.Top); // top
Inc(R.Top);
Inc(R.Left);
ACanvas.Line(R.Right-1, R.Top, R.Right-1, R.Bottom-1); // right
ACanvas.Line(R.Left, R.Bottom-1, R.Right-1, R.Bottom-1); // bottom
end;
end else begin
ACanvas.Pen.Color := clWindowFrame;
ACanvas.Brush.Color := clBtnFace;
ACanvas.Brush.Style := bsSolid;
ACanvas.Rectangle(R);
if IsFocused then begin
InflateRect(R, 1, 1);
ACanvas.Brush.Style := bsClear;
ACanvas.Pen.Color := clBlack;
ACanvas.Rectangle(R);
end;
InflateRect(R, -1, -1);
if not IsDown then begin
ACanvas.Pen.Color := clBtnHighlight;
ACanvas.Line(R.Left, R.Top, R.Left, R.Bottom); // left
ACanvas.Line(R.Left, R.Top, R.Right, R.Top); // top
ACanvas.Pen.Color := clBtnShadow;
ACanvas.Line(R.Right, R.Top, R.Right, R.Bottom); // right
ACanvas.Line(R.Left, R.Bottom, R.Right, R.Bottom); // bottom
end else begin
ACanvas.Pen.Color := clBtnShadow;
ACanvas.MoveTo(R.Left, R.Bottom-1);
ACanvas.LineTo(R.Left, R.Top);
ACanvas.LineTo(R.Right, R.Top);
end;
end;
Result := Rect(ARect.Left + 1, ARect.Top + 1, ARect.Right - 2, ARect.Bottom - 2);
if IsDown then OffsetRect(Result, 1, 1);
end;
//=== { TJvCustomTabBar } ====================================================
constructor TJvCustomTabBar.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle - [csAcceptsControls, csOpaque] {+ [csDesignInteractive]};
FTabs := TJvTabBarItems.Create(Self, TJvTabBarItem);
FChangeLink := TChangeLink.Create;
FChangeLink.OnChange := @ImagesChanged;
FOrientation := toTop;
FRightClickSelect := True;
FCloseButton := True;
FAutoFreeClosed := True;
FFlatScrollButtons := True;
FMargin := 6;
Align := alTop;
with GetControlClassDefaultSize do
SetInitialBounds(0, 0, CX, CY);
AutoSize := true;
end;
destructor TJvCustomTabBar.Destroy;
begin
// these events are too dangerous during object destruction
FOnTabSelected := nil;
FOnTabSelecting := nil;
FOnChange := nil;
Painter := nil;
Images := nil;
FChangeLink.Free;
FTabs.Free;
FTabs := nil;
FScrollButtonBackground.Free;
FScrollButtonBackground := nil;
inherited Destroy;
end;
procedure TJvCustomTabBar.LeftTabChanged;
begin
if Assigned(FOnLeftTabChange) then
FOnLeftTabChange(Self);
end;
procedure TJvCustomTabBar.Loaded;
begin
inherited Loaded;
SelectedTab := FindSelectableTab(nil);
UpdateScrollButtons;
end;
procedure TJvCustomTabBar.Notification(Component: TComponent; Operation: TOperation);
var
I: Integer;
begin
inherited Notification(Component, Operation);
if Operation = opRemove then
begin
if Component = FPainter then
Painter := nil
else
if Component = FImages then
Images := nil
else
if Component = FPageList then
PageList := nil;
end;
if FTabs <> nil then
for I := Tabs.Count - 1 downto 0 do
Tabs[I].Notification(Component, Operation);
end;
procedure TJvCustomTabBar.DrawScrollBarGlyph(ACanvas: TCanvas; X, Y: Integer; ALeft, Disabled: Boolean);
procedure OffsetPt(var Pt: TPoint; X, Y: Integer);
begin
Pt := Point(Pt.X + X, Pt.Y + Y);
end;
const
W = 4;
H = 7;
var
Pts: array [0..2] of TPoint;
savedBrush: TBrush;
savedPen: TPen;
begin
savedBrush := TBrush.Create;
savedPen := TPen.Create;
try
savedBrush.Assign(ACanvas.Brush);
savedPen.Assign(ACanvas.Pen);
if ALeft then
begin
Pts[0] := Point(X + W - 1, Y + 0);
Pts[1] := Point(X + W - 1, Y + H - 1);
Pts[2] := Point(X + 0, Y + (H - 1) div 2);
end
else
begin
Pts[0] := Point(X + 0, Y + 0);
Pts[1] := Point(X + 0, Y + H - 1);
Pts[2] := Point(X + W - 1, Y + (H - 1) div 2);
end;
ACanvas.Brush.Style := bsSolid;
if Disabled then
begin
ACanvas.Brush.Color := clWhite;
OffsetPt(Pts[0], 1, 1);
OffsetPt(Pts[1], 1, 1);
OffsetPt(Pts[2], 1, 1);
end
else
ACanvas.Brush.Color := clBlack;
ACanvas.Pen.Color := ACanvas.Brush.Color;
ACanvas.Polygon(Pts);
if Disabled then
begin
ACanvas.Brush.Color := clGray;
OffsetPt(Pts[0], -1, -1);
OffsetPt(Pts[1], -1, -1);
OffsetPt(Pts[2], -1, -1);
ACanvas.Pen.Color := ACanvas.Brush.Color;
ACanvas.Polygon(Pts);
end;
finally
ACanvas.Pen.Assign(savedPen);
ACanvas.Brush.Assign(savedBrush);
savedPen.Free;
savedBrush.Free;
end;
end;
procedure TJvCustomTabBar.SetTabs(Value: TJvTabBarItems);
begin
if Value <> FTabs then
FTabs.Assign(Value);
end;
procedure TJvCustomTabBar.SetPainter(Value: TJvTabBarPainter);
begin
if Value <> FPainter then
begin
if FPainter <> nil then
FPainter.FOnChangeList.Extract(Self);
ReplaceComponentReference(Self, Value, tComponent(FPainter));
if FPainter <> nil then
begin
FreeAndNil(FDefaultPainter);
FPainter.FOnChangeList.Add(Self);
if Parent <> nil then
UpdateScrollButtons;
end;
if not (csDestroying in ComponentState) then
Invalidate;
end;
end;
procedure TJvCustomTabBar.SetImages(Value: TCustomImageList);
begin
if ReplaceImageListReference(Self, Value, FImages, FChangeLink) then
if not (csDestroying in ComponentState) then
Invalidate;
end;
{$IF LCL_FullVersion >= 1090000}
procedure TJvCustomTabBar.SetImagesWidth(const AValue: Integer);
begin
if AValue = FImagesWidth then exit;
FImagesWidth := AValue;
Invalidate;
end;
{$ENDIF}
procedure TJvCustomTabBar.SetCloseButton(Value: Boolean);
begin
if Value <> FCloseButton then
begin
FCloseButton := Value;
Invalidate;
end;
end;
procedure TJvCustomTabBar.SetMargin(Value: Integer);
begin
if Value <> FMargin then
begin
FMargin := Value;
Invalidate;
end;
end;
procedure TJvCustomTabBar.SetSelectedTab(Value: TJvTabBarItem);
begin
if Value <> FSelectedTab then
begin
if (Value <> nil) and not Value.CanSelect then
Exit;
if TabSelecting(Value) then
begin
FSelectedTab := Value;
if not (csDestroying in ComponentState) then
Invalidate;
MakeVisible(FSelectedTab);
TabSelected(FSelectedTab);
end;
end;
end;
function TJvCustomTabBar.CurrentPainter: TJvTabBarPainter;
begin
Result := FPainter;
if Result = nil then
begin
if FDefaultPainter = nil then
FDefaultPainter := TJvModernTabBarPainter.Create(Self);
Result := FDefaultPainter;
end;
end;
function TJvCustomTabBar.TabClosing(Tab: TJvTabBarItem): Boolean;
begin
Result := True;
if Assigned(FOnTabClosing) then
FOnTabClosing(Self, Tab, Result);
end;
function TJvCustomTabBar.TabCloseQuery(Tab: TJvTabBarItem): Boolean;
begin
Result := True;
if Assigned(FOnTabCloseQuery) then
FOnTabCloseQuery(Self, Tab, Result);
end;
procedure TJvCustomTabBar.TabClosed(Tab: TJvTabBarItem);
begin
if AutoFreeClosed and not (csDesigning in ComponentState) then
Tab.Visible := False;
try
if Assigned(FOnTabClosed) then
FOnTabClosed(Self, Tab);
finally
// Do not double release if somebody "accidentally" released the Tab in TabClosed even if AutoFreeClosed is true
if AutoFreeClosed and not (csDesigning in ComponentState) and (FTabs.IndexOf(Tab) <> -1) then
Tab.Free;
end;
end;
function TJvCustomTabBar.TabSelecting(Tab: TJvTabBarItem): Boolean;
begin
Result := True;
if Assigned(FOnTabSelecting) then
FOnTabSelecting(Self, Tab, Result);
end;
procedure TJvCustomTabBar.TabSelected(Tab: TJvTabBarItem);
var
PageListIntf: IPageList;
begin
if (PageList <> nil) and Supports(PageList, IPageList, PageListIntf) then
begin
if Tab <> nil then
PageListIntf.SetActivePageIndex(Tab.Index)
else
PageListIntf.SetActivePageIndex(-1);
PageListIntf := nil; // who knows what OnTabSelected does with the PageList
end;
if Assigned(FOnTabSelected) then
FOnTabSelected(Self, Tab);
end;
function TJvCustomTabBar.FindSelectableTab(Tab: TJvTabBarItem): TJvTabBarItem;
var
Index: Integer;
begin
Result := Tab;
if (Result <> nil) and not Result.CanSelect then
begin
if AllowUnselected then
Result := nil
else
begin
Index := Result.Index + 1;
while Index < Tabs.Count do
begin
if Tabs[Index].CanSelect then
Break;
Inc(Index);
end;
if Index >= Tabs.Count then
begin
Index := Result.Index - 1;
while Index >= 0 do
begin
if Tabs[Index].CanSelect then
Break;
Dec(Index);
end;
end;
if Index >= 0 then
Result := Tabs[Index]
else
Result := nil;
end;
end;
if not AllowUnselected and not (Result <> nil) then
begin
// try to find a selectable tab
for Index := 0 to Tabs.Count - 1 do
if Tabs[Index].CanSelect then
begin
Result := Tabs[Index];
Break;
end;
end;
end;
procedure TJvCustomTabBar.Changed;
begin
if not (csDestroying in ComponentState) then
begin
// The TabSelected tab is now no more selectable
SelectedTab := FindSelectableTab(SelectedTab);
if Tabs.UpdateCount = 0 then
begin
Invalidate;
if Assigned(FOnChange) then
FOnChange(Self);
UpdateScrollButtons;
end;
end;
end;
procedure TJvCustomTabBar.ImagesChanged(Sender: TObject);
begin
if not (csDestroying in ComponentState) then
Invalidate;
end;
procedure TJvCustomTabBar.TabMoved(Tab: TJvTabBarItem);
begin
if Assigned(FOnTabMoved) then
FOnTabMoved(Self, Tab);
end;
procedure TJvCustomTabBar.DragOver(Source: TObject; X: Integer; Y: Integer;
State: TDragState; var Accept: Boolean);
var
InsertTab: TJvTabBarItem;
begin
if AllowTabMoving then
begin
InsertTab := TabAt(X, Y);
if InsertTab = nil then
if (LeftTab <> nil) and (X < LeftTab.FLeft) then
InsertTab := LeftTab
else
if Tabs.Count > 0 then
InsertTab := Tabs[Tabs.Count - 1];
Accept := (Source = Self) and (SelectedTab <> nil) and (InsertTab <> SelectedTab) and (InsertTab <> nil);
if Accept then
begin
if InsertTab <> FLastInsertTab then
begin
if FLastInsertTab <> nil then
Repaint;
{ Paint MoveDivider }
FLastInsertTab := InsertTab;
CurrentPainter.DrawMoveDivider(Canvas, InsertTab, InsertTab.Index < SelectedTab.Index);
end;
{ inherited DrawOver sets Accept to False if no event handler is assigned. }
if Assigned(OnDragOver) then
OnDragOver(Self, Source, X, Y, State, Accept);
Exit;
end
else
if FLastInsertTab <> nil then
begin
Repaint;
FLastInsertTab := nil;
end;
end;
inherited DragOver(Source, X, Y, State, Accept);
end;
procedure TJvCustomTabBar.DragCanceled;
begin
if FLastInsertTab <> nil then
Repaint;
FLastInsertTab := nil;
inherited DragCanceled;
end;
procedure TJvCustomTabBar.DragDrop(Source: TObject; X: Integer; Y: Integer);
var
InsertTab: TJvTabBarItem;
begin
if AllowTabMoving and (Source = Self) and (SelectedTab <> nil) then
begin
InsertTab := TabAt(X, Y);
if InsertTab = nil then
if (LeftTab <> nil) and (X < LeftTab.FLeft) then
InsertTab := LeftTab
else
InsertTab := Tabs[Tabs.Count - 1];
if InsertTab <> nil then
begin
SelectedTab.Index := InsertTab.Index;
TabMoved(SelectedTab);
SelectedTab.MakeVisible;
UpdateScrollButtons;
end;
end
else
if FLastInsertTab <> nil then
Repaint;
FLastInsertTab := nil;
inherited DragDrop(Source, X, Y);
end;
procedure TJvCustomTabBar.CMMouseLeave(var Msg: TLMessage);
begin
SetHotTab(nil);
inherited;
end;
procedure TJvCustomTabBar.WMEraseBkgnd(var Msg: TLMEraseBkgnd);
begin
Msg.Result := 1;
end;
function TJvCustomTabBar.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean;
begin
Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);
if not Result then
begin
Result := True;
if SelectedTab = nil then
SelectedTab := LeftTab;
if SelectedTab = nil then
Exit; // nothing to do
WheelDelta := WheelDelta div WHEEL_DELTA;
while WheelDelta <> 0 do
begin
if WheelDelta < 0 then
begin
if SelectedTab.GetNextVisible <> nil then
SelectedTab := SelectedTab.GetNextVisible
else
Break;
end
else
begin
if SelectedTab.GetPreviousVisible <> nil then
SelectedTab := SelectedTab.GetPreviousVisible
else
Break;
end;
if WheelDelta < 0 then
Inc(WheelDelta)
else
Dec(WheelDelta);
end;
end;
end;
procedure TJvCustomTabBar.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
Tab: TJvTabBarItem;
LastSelected: TJvTabBarItem;
begin
if ScrollButtonsMouseDown(Button, Shift, X, Y) then
Exit;
if Button = mbLeft then
begin
FMouseDownClosingTab := nil;
SetClosingTab(nil); // no tab should be closed
LastSelected := SelectedTab;
Tab := TabAt(X, Y);
if Tab <> nil then
SelectedTab := Tab;
if (Tab <> nil) and (Tab = SelectedTab) then
if CloseButton and (not SelectBeforeClose or (SelectedTab = LastSelected)) then
begin
if PtInRect(CurrentPainter.GetCloseRect(Canvas, Tab, Tab.DisplayRect), Point(X, Y)) then
begin
if TabClosing(Tab) then
begin
if FTabs.IndexOf(Tab) = -1 then
Tab := nil; // We should not keep a reference if somebody "accidentally" released the Tab in TabClosing
FMouseDownClosingTab := Tab;
SetClosingTab(Tab);
end;
inherited MouseDown(Button, Shift, X, Y);
Exit;
end;
end;
if (FClosingTab = nil) and AllowTabMoving and
([ssLeft, ssMiddle, ssRight] * Shift = [ssLeft]) then
BeginDrag(False);
end;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TJvCustomTabBar.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var
Pt: TPoint;
Tab: TJvTabBarItem;
begin
if ScrollButtonsMouseUp(Button, Shift, X, Y) then
Exit;
try
if RightClickSelect and not (PopupMenu <> nil) and (Button = mbRight) then
begin
Tab := TabAt(X, Y);
if Tab <> nil then
SelectedTab := Tab;
if (Tab <> nil) and (Tab.PopupMenu <> nil) then
begin
Pt := ClientToScreen(Point(X, Y));
Tab.PopupMenu.Popup(Pt.X, Pt.Y);
end;
end
else
if Button = mbLeft then
begin
if (FClosingTab <> nil) and CloseButton then
begin
CalcTabsRects;
if PtInRect(CurrentPainter.GetCloseRect(Canvas, FClosingTab, FClosingTab.DisplayRect), Point(X, Y)) then
begin
if TabCloseQuery(FClosingTab) then
TabClosed(FClosingTab)
end;
end;
end;
finally
FMouseDownClosingTab := nil;
SetClosingTab(nil);
end;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TJvCustomTabBar.MouseMove(Shift: TShiftState; X, Y: Integer);
var
Tab: TJvTabBarItem;
NewHint: TCaption;
begin
CalcTabsRects; // maybe inefficent
if ScrollButtonsMouseMove(Shift, X, Y) then
Exit;
Tab := TabAt(X, Y);
if HotTracking and ([ssLeft, ssMiddle, ssRight] * Shift = []) then
SetHotTab(Tab);
if CloseButton and (FMouseDownClosingTab <> nil) and (ssLeft in Shift) then
begin
if PtInRect(CurrentPainter.GetCloseRect(Canvas, FMouseDownClosingTab,
FMouseDownClosingTab.DisplayRect), Point(X, Y)) then
SetClosingTab(FMouseDownClosingTab)
else
SetClosingTab(nil)
end;
if (Tab <> nil) and Tab.ShowHint then
NewHint := Tab.Hint
else
NewHint := FHint;
if NewHint <> inherited Hint then
begin
Application.CancelHint;
ShowHint := False;
ShowHint := True;
inherited Hint := NewHint;
end;
inherited MouseMove(Shift, X, Y);
end;
function TJvCustomTabBar.ScrollButtonsMouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer): Boolean;
function HandleButton(Kind: TJvTabBarScrollButtonKind; var State: TJvTabBarScrollButtonState;
X, Y: Integer; const R: TRect): Boolean;
begin
Result := PtInRect(R, Point(X, Y));
case State of
sbsNormal, sbsHot:
begin
if Result then
begin
State := sbsPressed;
PaintScrollButtons;
if FRepeatTimer = nil then
FRepeatTimer := TTimer.Create(Self);
FRepeatTimer.OnTimer := @TimerExpired;
FRepeatTimer.Interval := 400;
FRepeatTimer.Enabled := True;
FRepeatTimer.Tag := Integer(Kind);
FScrollRepeatedClicked := False;
end;
end;
end;
end;
begin
Result := False;
if (FBtnLeftScroll.State <> sbsHidden) then
Result := HandleButton(sbScrollLeft, FBtnLeftScroll.State, X, Y, FBtnLeftScroll.Rect);
if not Result and (FBtnRightScroll.State <> sbsHidden) then
Result := HandleButton(sbScrollRight, FBtnRightScroll.State, X, Y, FBtnRightScroll.Rect);
end;
function TJvCustomTabBar.ScrollButtonsMouseMove(Shift: TShiftState; X, Y: Integer): Boolean;
function HandleButton(var ExState: Boolean; var State: TJvTabBarScrollButtonState;
X, Y: Integer; const R: TRect): Boolean;
begin
Result := PtInRect(R, Point(X, Y));
case State of
sbsNormal:
begin
if Result then
begin
State := sbsHot;
PaintScrollButtons;
Result := True;
end;
end;
sbsPressed:
begin
if not Result then
begin
ExState := True;
State := sbsNormal;
PaintScrollButtons;
State := sbsPressed;
end
else
begin
if ExState then
begin
ExState := False;
PaintScrollButtons;
end;
end;
end;
sbsHot:
begin
if not Result then
begin
State := sbsNormal;
PaintScrollButtons;
end;
end;
end;
end;
begin
Result := False;
if (FBtnLeftScroll.State <> sbsHidden) then
Result := HandleButton(FBtnLeftScroll.ExState, FBtnLeftScroll.State, X, Y, FBtnLeftScroll.Rect);
if not Result and (FBtnRightScroll.State <> sbsHidden) then
Result := HandleButton(FBtnRightScroll.ExState, FBtnRightScroll.State, X, Y, FBtnRightScroll.Rect);
end;
function TJvCustomTabBar.ScrollButtonsMouseUp(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer): Boolean;
function HandleButton(Kind: TJvTabBarScrollButtonKind; var State: TJvTabBarScrollButtonState;
X, Y: Integer; const R: TRect): Boolean;
begin
Result := PtInRect(R, Point(X, Y));
case State of
sbsPressed:
begin
FreeAndNil(FRepeatTimer);
State := sbsNormal;
PaintScrollButtons;
if Result and not FScrollRepeatedClicked then
ScrollButtonClick(Kind);
FScrollRepeatedClicked := False;
end;
end;
end;
begin
Result := False;
if (FBtnLeftScroll.State <> sbsHidden) then
Result := HandleButton(sbScrollLeft, FBtnLeftScroll.State, X, Y, FBtnLeftScroll.Rect);
if not Result and (FBtnRightScroll.State <> sbsHidden) then
Result := HandleButton(sbScrollRight, FBtnRightScroll.State, X, Y, FBtnRightScroll.Rect);
end;
procedure TJvCustomTabBar.TimerExpired(Sender: TObject);
var
Kind: TJvTabBarScrollButtonKind;
State: TJvTabBarScrollButtonState;
begin
FRepeatTimer.Interval := 100;
Kind := TJvTabBarScrollButtonKind(FRepeatTimer.Tag);
case Kind of
sbScrollLeft:
State := FBtnLeftScroll.State;
sbScrollRight:
State := FBtnRightScroll.State;
else
Exit;
end;
if (State = sbsPressed) and Enabled {and MouseCapture} then
begin
try
FScrollRepeatedClicked := True;
ScrollButtonClick(Kind);
case Kind of
sbScrollLeft:
if not (FBtnLeftScroll.State in [sbsHidden, sbsDisabled]) then
FBtnLeftScroll.State := sbsPressed;
sbScrollRight:
if not (FBtnRightScroll.State in [sbsHidden, sbsDisabled]) then
FBtnRightScroll.State := sbsPressed;
end;
except
FRepeatTimer.Enabled := False;
raise;
end;
end
else
FreeAndNil(FRepeatTimer);
end;
procedure TJvCustomTabBar.SetHotTab(Tab: TJvTabBarItem);
begin
if (csDestroying in ComponentState) or not HotTracking then
FHotTab := nil
else
if Tab <> FHotTab then
begin
FHotTab := Tab;
if poPaintsHotTab in CurrentPainter.Options then
Paint;
end;
end;
function TJvCustomTabBar.CloseTab(ATab: TJvTabBarItem): Boolean;
begin
Result := False;
if ATab <> nil then
begin
FClosingTab := ATab;
try
Result := TabCloseQuery(FClosingTab);
if Result then
TabClosed(FClosingTab);
finally
FClosingTab := nil;
end;
end;
end;
function TJvCustomTabBar.AddTab(const ACaption: string): TJvTabBarItem;
begin
Result := TJvTabBarItem(Tabs.Add);
Result.Caption := ACaption;
end;
function TJvCustomTabBar.FindTab(const ACaption: string): TJvTabBarItem;
var
i: Integer;
begin
for i := 0 to Tabs.Count - 1 do
if ACaption = Tabs[i].Caption then
begin
Result := Tabs[i];
Exit;
end;
Result := nil;
end;
procedure TJvCustomTabBar.CalcTabsRects;
var
I, X: Integer;
Tab: TJvTabBarItem;
Offset: Integer;
Index: Integer;
begin
if csDestroying in ComponentState then
Exit;
Offset := 0;
X := Margin; // adjust for scrolled area
Index := 0;
for I := 0 to Tabs.Count - 1 do
begin
Tab := Tabs[I];
if Tab.Visible then
begin
Tab.FLeft := X;
Inc(X, GetTabWidth(Tab));
Inc(X, CurrentPainter.GetDividerWidth(Canvas, Tab));
if Index < FLeftIndex then
begin
Inc(Offset, X); // this tab is placed too left.
X := 0;
Tab.FLeft := -Offset - 10;
end;
Inc(Index);
end
else
Tab.FLeft := -1;
end;
FRequiredWidth := X + Offset;
FLastTabRight := X;
end;
procedure TJvCustomTabBar.CalculatePreferredSize(
var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean);
var
tabSize: TSize;
imgSize: TSize;
h: Integer;
{$IF LCL_FullVersion >= 1090000}
imgRes: TScaledImageListResolution;
{$ENDIF}
begin
CurrentPainter.AutoSize;
// Text height
Canvas.Font.Assign(Font);
PreferredHeight := Canvas.TextHeight('Tg');
// Icon height
if FImages <> nil then begin
{$IF LCL_FullVersion >= 1090000}
imgRes := FImages.ResolutionForPPI[FImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor];
h := imgRes.Height;
if imgRes.Height > PreferredHeight then
PreferredHeight := imgRes.Height;
{$ELSE}
h := Images.Height;
{$ENDIF}
if h > PReferredHeight then
PreferredHeight := h;
end;
// Close button height
if FCloseButton then begin
h := Scale96ToForm(CLOSE_BUTTON_SIZE);
if h > PreferredHeight then
PreferredHeight := h;
end;
// Margins
inc(PreferredHeight, Scale96ToForm(TOP_MARGIN) + Scale96ToForm(BOTTOM_MARGIN));
end;
procedure TJvCustomTabBar.Paint;
var
I: Integer;
Bmp: TBitmap;
R: TRect;
begin
CalcTabsRects;
Bmp := TBitmap.Create;
try
Bmp.Width := ClientWidth;
Bmp.Height := ClientHeight;
CurrentPainter.DrawBackground(Bmp.Canvas, Self, ClientRect);
if (FBtnLeftScroll.State <> sbsHidden) and (FBtnRightScroll.State <> sbsHidden) then
begin
if FScrollButtonBackground = nil then
FScrollButtonBackground := TBitmap.Create;
FScrollButtonBackground.Width := Bmp.Width - FBarWidth;
FScrollButtonBackground.Height := Bmp.Height;
R := Rect(FBarWidth, 0, Bmp.Width, Bmp.Height);
FScrollButtonBackground.Canvas.CopyRect(Rect(0, 0, FScrollButtonBackground.Width, R.Bottom), Bmp.Canvas, R);
PaintScrollButtons;
if FBarWidth > 0 then
Bmp.Width := FBarWidth;
end;
if FBarWidth > 0 then
for I := 0 to Tabs.Count - 1 do
if Tabs[I].Visible then
PaintTab(Bmp.Canvas, Tabs[I]);
Canvas.Draw(0, 0, Bmp);
finally
Bmp.Free;
end;
end;
procedure TJvCustomTabBar.PaintTab(ACanvas: TCanvas; Tab: TJvTabBarItem);
var
R: TRect;
begin
if csDestroying in ComponentState then
Exit;
if Tab.Visible then
begin
R := Tab.DisplayRect;
if (R.Right >= 0) and (R.Left < FBarWidth) then
begin
CurrentPainter.DrawTab(ACanvas, Tab, R);
R.Left := R.Right;
R.Right := R.Left + CurrentPainter.GetDividerWidth(ACanvas, Tab) - 1;
CurrentPainter.DrawDivider(ACanvas, Tab, R);
end;
end;
end;
procedure TJvCustomTabBar.PaintScrollButtons;
begin
if not HandleAllocated then
exit;
if (FScrollButtonBackground = nil) and Visible then
Paint
else // paint scroll button's background and the buttons
Canvas.Draw(FBarWidth, 0, FScrollButtonBackground);
CurrentPainter.DrawScrollButton(Canvas, Self, sbScrollLeft, FBtnLeftScroll.State, FBtnLeftScroll.Rect);
CurrentPainter.DrawScrollButton(Canvas, Self, sbScrollRight, FBtnRightScroll.State, FBtnRightScroll.Rect);
end;
class function TJvCustomTabBar.GetControlClassDefaultSize: TSize;
begin
Result.CX := 100;
Result.CY := 24;
end;
function TJvCustomTabBar.GetTabHeight(Tab: TJvTabBarItem): Integer;
begin
Result := CurrentPainter.GetTabSize(Canvas, Tab).cy;
end;
function TJvCustomTabBar.GetTabWidth(Tab: TJvTabBarItem): Integer;
begin
Result := CurrentPainter.GetTabSize(Canvas, Tab).cx;
end;
function TJvCustomTabBar.TabAt(X, Y: Integer): TJvTabBarItem;
var
I: Integer;
Pt: TPoint;
begin
if (FBtnLeftScroll.State = sbsHidden) or (X < FBarWidth) then
begin
CalcTabsRects;
Pt := Point(X, Y);
for I := 0 to Tabs.Count - 1 do
if PtInRect(Tabs[I].DisplayRect, Pt) then
begin
Result := Tabs[I];
Exit;
end;
end;
Result := nil;
end;
procedure TJvCustomTabBar.SetClosingTab(Tab: TJvTabBarItem);
begin
if Tab <> FClosingTab then
begin
FClosingTab := Tab; // this tab should be TabClosed
Paint;
end;
end;
function TJvCustomTabBar.GetLeftTab: TJvTabBarItem;
begin
if (Tabs <> nil) and (FLeftIndex < Tabs.Count) then
begin
Result := Tabs[FLeftIndex];
if not Result.Visible then
Result := Result.GetNextVisible;
end
else
Result := nil;
end;
procedure TJvCustomTabBar.SetLeftTab(Value: TJvTabBarItem);
var
Index: Integer;
Tab: TJvTabBarItem;
begin
Index := 0;
if Value <> nil then
begin
// find first visible before or at Value.Index
if (Tabs <> nil) and (Tabs.Count > 0) and (Value <> Tabs[0]) then
begin
while Index < Tabs.Count do
begin
Tab := Tabs[Index].GetNextVisible;
if Tab = nil then
begin
Index := FLeftIndex; // do not change
Break;
end
else
begin
Index := Tab.Index;
if Tab.Index >= Value.Index then
Break;
end;
end;
if Index >= Tabs.Count then
Index := FLeftIndex; // do not change
end;
end;
if Index <> FLeftIndex then
begin
FLeftIndex := Index;
Invalidate;
UpdateScrollButtons;
LeftTabChanged;
end;
end;
procedure TJvCustomTabBar.UpdateScrollButtons;
const
State: array[Boolean] of TJvTabBarScrollButtonState = (sbsDisabled, sbsNormal);
BtnSize = 12;
begin
CalcTabsRects;
if (FRequiredWidth < ClientWidth) or ((FLeftIndex = 0) and
(FLastTabRight <= ClientWidth)) then
begin
FBtnLeftScroll.State := sbsHidden;
FBtnRightScroll.State := sbsHidden;
FLeftIndex := 0;
FBarWidth := ClientWidth;
Invalidate;
end
else
begin
FBtnLeftScroll.State := sbsNormal;
FBtnRightScroll.State := sbsNormal;
if poBottomScrollButtons in CurrentPainter.Options then
begin
FBtnLeftScroll.Rect := Bounds(ClientWidth - BtnSize * 2 - 1 - 1,
ClientHeight - BtnSize - 2, BtnSize, BtnSize);
FBtnRightScroll.Rect := Bounds(FBtnLeftScroll.Rect.Right,
ClientHeight - BtnSize - 2, BtnSize, BtnSize);
end
else
begin
FBtnLeftScroll.Rect := Bounds(ClientWidth - BtnSize * 2 - 1 - 1, 2, BtnSize, BtnSize);
FBtnRightScroll.Rect := Bounds(FBtnLeftScroll.Rect.Right, 2, BtnSize, BtnSize);
end;
if not FlatScrollButtons then
OffsetRect(FBtnRightScroll.Rect, -1, 0);
//CurrentPainter.GetScrollButtons(Self, FBtnLeftScroll.Rect, FBtnRightScroll.Rect);
FBarWidth := FBtnLeftScroll.Rect.Left - 2;
FBtnLeftScroll.State := State[FLeftIndex > 0];
FBtnRightScroll.State := State[FLastTabRight >= ClientWidth];
PaintScrollButtons;
end;
end;
procedure TJvCustomTabBar.Resize;
begin
UpdateScrollButtons;
inherited Resize;
end;
procedure TJvCustomTabBar.ScrollButtonClick(Button: TJvTabBarScrollButtonKind);
begin
if Button = sbScrollLeft then
begin
if FBtnLeftScroll.State in [sbsHidden, sbsDisabled] then
Exit;
Dec(FLeftIndex);
end
else
if Button = sbScrollRight then
begin
if FBtnRightScroll.State in [sbsHidden, sbsDisabled] then
Exit;
Inc(FLeftIndex);
end;
UpdateScrollButtons;
Invalidate;
if Assigned(FOnScrollButtonClick) then
FOnScrollButtonClick(Self, Button);
LeftTabChanged;
end;
function TJvCustomTabBar.MakeVisible(Tab: TJvTabBarItem): Boolean;
var
R: TRect;
LastLeftIndex: Integer;
AtLeft: Boolean;
begin
Result := False;
if (Tab = nil) or not Tab.Visible then
Exit;
LastLeftIndex := FLeftIndex;
if FBarWidth > 0 then
begin
AtLeft := False;
repeat
CalcTabsRects;
R := Tab.DisplayRect;
if (R.Right > FBarWidth) and not AtLeft then
Inc(FLeftIndex)
else
if R.Left < 0 then
begin
Dec(FLeftIndex);
AtLeft := True; // prevent an endless loop
end
else
Break;
until FLeftIndex = Tabs.Count - 1;
end
else
FLeftIndex := 0;
if (R.Left < 0) and (FLeftIndex > 0) then
Dec(FLeftIndex); // bar is too small
if FLeftIndex <> LastLeftIndex then
begin
UpdateScrollButtons;
Invalidate;
LeftTabChanged;
end;
end;
function TJvCustomTabBar.FindData(Data: TObject): TJvTabBarItem;
var
I: Integer;
begin
for I := 0 to Tabs.Count - 1 do
if Tabs[I].Data = Data then
begin
Result := Tabs[I];
Exit;
end;
Result := nil;
end;
procedure TJvCustomTabBar.SetHint(const Value: TCaption);
begin
if Value <> FHint then
FHint := Value;
end;
procedure TJvCustomTabBar.SetFlatScrollButtons(const Value: Boolean);
begin
if Value <> FFlatScrollButtons then
begin
FFlatScrollButtons := Value;
FBtnLeftScroll.State := sbsHidden;
FBtnRightScroll.State := sbsHidden;
UpdateScrollButtons;
end;
end;
function TJvCustomTabBar.GetHeight: Integer;
begin
Result := inherited Height;
end;
procedure TJvCustomTabBar.SetHeight(AValue: Integer);
begin
if AValue = GetHeight then exit;
AutoSize := false;
inherited Height := AValue;
end;
procedure TJvCustomTabBar.SetPageList(const Value: TCustomControl);
var
PageListIntf: IPageList;
begin
if Value <> FPageList then
begin
if Value <> nil then
begin
if not Supports(Value, IPageList, PageListIntf) then
Exit;
if SelectedTab <> nil then
PageListIntf.SetActivePageIndex(SelectedTab.Index)
else
PageListIntf.SetActivePageIndex(0);
PageListIntf := nil;
end;
if FPageList <> nil then
FPageList.RemoveFreeNotification(Self);
FPageList := Value;
if FPageList <> nil then
FPageList.FreeNotification(Self);
end;
end;
procedure TJvCustomTabBar.SetOrientation(const Value: TJvTabBarOrientation);
begin
if Value <> FOrientation then
begin
FOrientation := Value;
CalcTabsRects;
Repaint;
end;
end;
//=== { TJvTabBarItem } ======================================================
constructor TJvTabBarItem.Create(ACollection: Classes.TCollection);
begin
inherited Create(ACollection);
FImageIndex := -1;
FEnabled := True;
FVisible := True;
FShowHint := True;
end;
destructor TJvTabBarItem.Destroy;
begin
PopupMenu := nil;
Visible := False; // CanSelect returns false
FAutoDeleteDatas.Free;
inherited Destroy;
end;
procedure TJvTabBarItem.Assign(Source: TPersistent);
begin
if Source is TJvTabBarItem then
begin
with TJvTabBarItem(Source) do
begin
Self.FImageIndex := FImageIndex;
Self.FEnabled := FEnabled;
Self.FVisible := FVisible;
Self.FTag := FTag;
Self.FData := FData;
Self.FHint := FHint;
Self.FShowHint := FShowHint;
Self.FName := FName;
Self.FCaption := FCaption;
Self.FModified := FModified;
Self.FImages := FImages;
Changed;
end;
end
else
inherited Assign(Source);
end;
procedure TJvTabBarItem.Notification(Component: TComponent;
Operation: TOperation);
begin
if Operation = opRemove then
if Component = PopupMenu then
PopupMenu := nil;
end;
procedure TJvTabBarItem.Changed;
begin
TabBar.Changed;
end;
function TJvTabBarItem.GetDisplayRect: TRect;
begin
if not Visible then
Result := Rect(-1, -1, -1, -1)
else
begin
if FLeft = -1 then
TabBar.CalcTabsRects; // not initialized
case TabBar.Orientation of
toBottom:
Result := Rect(
FLeft,
0,
FLeft + TabBar.GetTabWidth(Self),
0 + TabBar.GetTabHeight(Self)
);
else
// toTop
Result := Rect(
FLeft,
TabBar.ClientHeight - TabBar.GetTabHeight(Self),
FLeft + TabBar.GetTabWidth(Self),
TabBar.ClientHeight
);
end;
end;
end;
function TJvTabBarItem.GetHot: Boolean;
begin
Result := (TabBar.HotTab = Self);
end;
function TJvTabBarItem.GetImages: TCustomImageList;
begin
Result := TabBar.Images;
end;
function TJvTabBarItem.GetSelected: Boolean;
begin
Result := TabBar.SelectedTab = Self;
end;
function TJvTabBarItem.GetTabBar: TJvCustomTabBar;
begin
Result := (GetOwner as TJvTabBarItems).TabBar;
end;
procedure TJvTabBarItem.SetCaption(const Value: TCaption);
var
PageListIntf: IPageList;
begin
if Value <> FCaption then
begin
FCaption := Value;
if TabBar.PageListTabLink and (TabBar.PageList <> nil) and
not (csLoading in TabBar.ComponentState) and
Supports(TabBar.PageList, IPageList, PageListIntf)
then
PageListIntf.PageCaptionChanged(Index, FCaption);
Changed;
end;
end;
procedure TJvTabBarItem.SetEnabled(const Value: Boolean);
begin
if Value <> FEnabled then
begin
FEnabled := Value;
Changed;
end;
end;
procedure TJvTabBarItem.SetImageIndex(const Value: TImageIndex);
begin
if Value <> FImageIndex then
begin
FImageIndex := Value;
Changed;
end;
end;
procedure TJvTabBarItem.SetName(const Value: string);
begin
if (Value <> FName) and (TJvTabBarItems(Collection).Find(Value) = nil) then
FName := Value;
end;
procedure TJvTabBarItem.SetSelected(const Value: Boolean);
begin
if Value then
TabBar.SelectedTab := Self;
end;
procedure TJvTabBarItem.SetVisible(const Value: Boolean);
begin
if Value <> FVisible then
begin
FVisible := Value;
FLeft := -1; // discard
Changed;
end;
end;
function TJvTabBarItem.CanSelect: Boolean;
begin
Result := Visible and Enabled;
end;
function TJvTabBarItem.GetNextVisible: TJvTabBarItem;
var
I: Integer;
begin
for I := Index + 1 to TabBar.Tabs.Count - 1 do
if TabBar.Tabs[I].Visible then
begin
Result := TabBar.Tabs[I];
Exit;
end;
Result := nil;
end;
function TJvTabBarItem.GetPreviousVisible: TJvTabBarItem;
var
I: Integer;
begin
for I := Index - 1 downto 0 do
if TabBar.Tabs[I].Visible then
begin
Result := TabBar.Tabs[I];
Exit;
end;
Result := nil;
end;
function TJvTabBarItem.AutoDeleteData: TObjectList;
begin
if FAutoDeleteDatas = nil then
FAutoDeleteDatas := TObjectList.Create;
Result := FAutoDeleteDatas;
end;
function TJvTabBarItem.GetClosing: Boolean;
begin
Result := TabBar.ClosingTab = Self;
end;
procedure TJvTabBarItem.SetModified(const Value: Boolean);
begin
if Value <> FModified then
begin
FModified := Value;
Changed;
end;
end;
procedure TJvTabBarItem.SetPopupMenu(const Value: TPopupMenu);
begin
if Value <> FPopupMenu then
begin
if FPopupMenu <> nil then
FPopupMenu.RemoveFreeNotification(TabBar);
FPopupMenu := Value;
if FPopupMenu <> nil then
FPopupMenu.FreeNotification(TabBar);
end;
end;
procedure TJvTabBarItem.MakeVisible;
begin
TabBar.MakeVisible(Self);
end;
function TJvTabBarItem.GetEnabled: Boolean;
begin
Result := FEnabled;
if Assigned(FOnGetEnabled) then
FOnGetEnabled(Self, Result);
end;
function TJvTabBarItem.GetModified: Boolean;
begin
Result := FModified;
if Assigned(FOnGetModified) then
FOnGetModified(Self, Result);
end;
procedure TJvTabBarItem.SetIndex(Value: Integer);
var
PageListIntf: IPageList;
LastIndex: Integer;
begin
LastIndex := Index;
inherited SetIndex(Value);
if TabBar.PageListTabLink and (LastIndex <> Index) and (TabBar.PageList <> nil) and
not (csLoading in TabBar.ComponentState) and
Supports(TabBar.PageList, IPageList, PageListIntf) then
PageListIntf.MovePage(LastIndex, Index);
Changed;
end;
//=== { TJvTabBarItems } =====================================================
procedure TJvTabBarItems.EndUpdate;
begin
inherited EndUpdate;
if UpdateCount = 0 then
TabBar.Changed;
end;
function TJvTabBarItems.Find(const AName: string): TJvTabBarItem;
var
I: Integer;
begin
Result := nil;
for I := 0 to Count - 1 do
if Items[I].Name = AName then
begin
Result := Items[I];
Break;
end;
end;
function TJvTabBarItems.GetTabBar: TJvCustomTabBar;
begin
Result := GetOwner as TJvCustomTabBar;
end;
function TJvTabBarItems.GetItem(Index: Integer): TJvTabBarItem;
begin
Result := TJvTabBarItem(inherited Items[Index]);
end;
procedure TJvTabBarItems.SetItem(Index: Integer; const Value: TJvTabBarItem);
begin
if Value <> GetItem(Index) then
GetItem(Index).Assign(Value);
end;
procedure TJvTabBarItems.Notify(Item: TCollectionItem; Action: TCollectionNotification);
var
PageListIntf: IPageList;
begin
inherited Notify(Item, Action);
if Action in [cnExtracting, cnDeleting] then
begin
// unselect the item to delete
if TabBar.SelectedTab = Item then
TabBar.SelectedTab := nil;
if TabBar.HotTab = Item then
TabBar.SetHotTab(nil);
if TabBar.FMouseDownClosingTab = Item then
TabBar.FMouseDownClosingTab := nil;
if TabBar.ClosingTab = Item then
TabBar.FClosingTab := nil;
if TabBar.FLastInsertTab = Item then
TabBar.FLastInsertTab := nil;
if not (csDestroying in TabBar.ComponentState) and (TabBar.LeftTab = Item) then
TabBar.LeftTab := TabBar.LeftTab.GetPreviousVisible;
end;
if TabBar.PageListTabLink and (TabBar.PageList <> nil) and
not (csLoading in TabBar.ComponentState) and
Supports(TabBar.PageList, IPageList, PageListIntf) then
begin
case Action of
cnAdded:
PageListIntf.AddPage(TJvTabBarItem(Item).Caption);
cnExtracting, cnDeleting:
PageListIntf.DeletePage(TJvTabBarItem(Item).Index);
end;
end;
TabBar.Changed;
end;
function TJvTabBarItems.IndexOf(Item: TJvTabBarItem): Integer;
begin
for Result := 0 to Count - 1 do
if Items[Result] = Item then
Exit;
Result := -1;
end;
//=== { TJvTabBarPainter } ===================================================
constructor TJvTabBarPainter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FOnChangeList := TList.Create;
end;
destructor TJvTabBarPainter.Destroy;
begin
inherited Destroy; // invokes TJvTabBar.Notification that accesses FOnChangeList
FOnChangeList.Free;
end;
procedure TJvTabBarPainter.AutoSize;
begin
end;
procedure TJvTabBarPainter.Changed;
var
i: Integer;
begin
for i := 0 to FOnChangeList.Count - 1 do
TJvCustomTabBar(FOnChangeList[i]).ImagesChanged(Self);
end;
function TJvTabBarPainter.GetRealImageSize(ATab: TJvTabBarItem): TSize;
{$IF LCL_FullVersion >= 1090000}
var
imgRes: TScaledImageListResolution;
tabBar: TJvCustomTabBar;
f: Double;
ppi: Integer;
begin
tabBar := GetTabBar(ATab);
f := tabBar.GetCanvasScaleFactor;
ppi := GetPixelsPerInch;
imgRes := ATab.GetImages.ResolutionForPPI[tabBar.ImagesWidth, ppi, f];
Result.CX := imgRes.Width;
Result.CY := imgRes.Height;
end;
{$ELSE}
begin
Result.CX := ATab.GetImages.Width;
Result.CY := ATab.GetImages.Height;
end;
{$ENDIF}
procedure TJvTabBarPainter.GetScrollButtons(TabBar: TJvCustomTabBar; var LeftButton, RightButton: TRect);
begin
{ reserved for future use }
end;
function TJvTabBarPainter.GetTabBar(ATab: TJvTabBarItem): TJvCustomTabBar;
begin
Result := TJvTabBarItems(ATab.Collection).TabBar;
end;
procedure TJvTabBarPainter.DrawScrollButton(Canvas: TCanvas;
TabBar: TJvCustomTabBar; Button: TJvTabBarScrollButtonKind;
State: TJvTabBarScrollButtonState; R: TRect);
{$IFDEF JVCLThemesEnabled}
const
SCROLL: array[TJvTabBarScrollButtonKind, TJvTabBarScrollButtonState] of TThemedScrollbar = (
// sbsHidden sbsNormal sbsHot sbsPressed sbsDisabled
(tsArrowBtnLeftNormal, tsArrowBtnLeftNormal, tsArrowBtnLeftHot, tsArrowBtnLeftPressed, tsArrowBtnLeftDisabled),
(tsArrowBtnRightNormal, tsArrowBtnRightNormal, tsArrowBtnRightHot, tsArrowBtnRightPressed, tsArrowBtnRightDisabled)
);
var
details: TThemedElementDetails;
{$ENDIF JVCLThemesEnabled}
begin
{$IFDEF JVCLThemesEnabled}
if ThemeServices.ThemesEnabled then begin
details := ThemeServices.GetElementDetails(SCROLL[Button, State]);
ThemeServices.DrawElement(Canvas.Handle, details, R, nil);
end else
{$ENDIF JVCLThemesEnabled}
begin
DrawButtonFace(Canvas, R, TabBar.FlatScrollButtons, State = sbsPressed, false);
if State = sbsPressed then
OffsetRect(R, 1, 1);
TabBar.DrawScrollBarGlyph(Canvas,
R.Left + (R.Right - R.Left - 4) div 2,
R.Top + (R.Bottom - R.Top - 7) div 2,
Button = sbScrollLeft, State = sbsDisabled);
end;
end;
function TJvTabBarPainter.Scale96(AValue: Integer): Integer;
begin
Result := MulDiv(AValue, GetPixelsPerInch, 96);
end;
//=== { TJvModernTabBarPainter } =============================================
constructor TJvModernTabBarPainter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FFont := TFont.Create;
FDisabledFont := TFont.Create;
FSelectedFont := TFont.Create;
FFont.Color := clWindowText;
FDisabledFont.Color := clGrayText;
FSelectedFont.Assign(FFont);
FFont.OnChange := @FontChanged;
FDisabledFont.OnChange := @FontChanged;
FSelectedFont.OnChange := @FontChanged;
FTabColor := clBtnFace;
FColor := clWindow;
FBorderColor := clSilver;
FControlDivideColor := clBlack;
FModifiedCrossColor := clRed;
FCloseColorSelected := $F4F4F4;
FCloseColor := clWhite;
FCloseCrossColorSelected := clBlack;
FCloseCrossColor := $5D5D5D;
FCloseCrossColorDisabled := $ADADAD;
FCloseRectColor := $868686;
FCloseRectColorDisabled := $D6D6D6;
FDividerColor := $99A8AC;
FMoveDividerColor := clBlack;
end;
destructor TJvModernTabBarPainter.Destroy;
begin
FFont.Free;
FDisabledFont.Free;
FSelectedFont.Free;
inherited Destroy;
end;
procedure TJvModernTabBarPainter.AutoSize;
begin
FTabHeight := 0;
FTabWidth := 0;
end;
procedure TJvModernTabBarPainter.DrawBackground(Canvas: TCanvas; TabBar: TJvCustomTabBar; R: TRect);
begin
with Canvas do
begin
Brush.Style := bsSolid;
Brush.Color := Color;
FillRect(R);
Brush.Style := bsClear;
Pen.Color := BorderColor;
Pen.Width := 1;
if TabBar.Orientation = toBottom then
begin
MoveTo(0, R.Bottom - 1);
LineTo(0, 0);
Pen.Color := ControlDivideColor;
LineTo(R.Right - 1, 0);
Pen.Color := BorderColor;
LineTo(R.Right - 1, R.Bottom - 1);
LineTo(0, R.Bottom - 1);
end
else // toTop
begin
MoveTo(0, R.Bottom - 1);
LineTo(0, 0);
LineTo(R.Right - 1, 0);
LineTo(R.Right - 1, R.Bottom - 1);
Pen.Color := ControlDivideColor;
LineTo(0, R.Bottom - 1);
end;
end;
end;
procedure TJvModernTabBarPainter.DrawDivider(Canvas: TCanvas; LeftTab: TJvTabBarItem; R: TRect);
begin
if not LeftTab.Selected then
begin
if (LeftTab.TabBar.SelectedTab = nil) or
(LeftTab.GetNextVisible <> LeftTab.TabBar.SelectedTab) then
begin
with Canvas do
begin
Pen.Color := DividerColor;
Pen.Width := 1;
MoveTo(R.Right - 1, R.Top + 3);
LineTo(R.Right - 1, R.Bottom - 3);
end;
end;
end;
end;
procedure TJvModernTabBarPainter.DrawMoveDivider(Canvas: TCanvas; Tab: TJvTabBarItem; MoveLeft: Boolean);
var
R: TRect;
begin
with Canvas do
begin
R := Tab.DisplayRect;
Inc(R.Top, 4);
Dec(R.Bottom, 2);
if MoveLeft then
begin
Dec(R.Left);
R.Right := R.Left + 4
end
else
begin
Dec(R.Right, 1);
R.Left := R.Right - 4;
end;
Brush.Color := MoveDividerColor;
FillRect(R);
end;
end;
procedure TJvModernTabBarPainter.DrawTab(Canvas: TCanvas; Tab: TJvTabBarItem;
ATabRect: TRect);
var
R, CloseR: TRect;
ts: TTextStyle;
margin: Integer;
x, y: Integer;
imgsize: TSize;
{$IF LCL_FullVersion >= 1090000}
imageRes: TScaledImageListResolution;
f: Double;
ppi: Integer;
tabBar: TJvCustomTabBar;
{$ENDIF}
begin
R := ATabRect;
with Canvas do
begin
Brush.Style := bsSolid;
Brush.Color := Color;
Pen.Mode := pmCopy;
Pen.Style := psSolid;
Pen.Width := 1;
if Tab.Selected then
begin
Brush.Style := bsSolid;
Brush.Color := TabColor;
FillRect(R);
Pen.Color := ControlDivideColor;
if Tab.TabBar.Orientation = toBottom then
begin
MoveTo(R.Left, R.Top);
LineTo(R.Left, R.Bottom - 1);
LineTo(R.Right - 1, R.Bottom - 1);
LineTo(R.Right - 1, R.Top - 1{end});
end
else // toTop
begin
MoveTo(R.Left, R.Bottom - 1);
LineTo(R.Left, R.Top);
LineTo(R.Right - 1, R.Top);
LineTo(R.Right - 1, R.Bottom - 1 + 1{end});
end;
end;
if Tab.Enabled and not Tab.Selected and Tab.Hot then
begin
// hot
Pen.Color := DividerColor;
MoveTo(R.Left, R.Top);
LineTo(R.Right - 1 - 1, R.Top);
end;
inc(R.Left, Scale96(LEFT_MARGIN));
dec(R.Right, Scale96(RIGHT_MARGIN));
if Tab.TabBar.CloseButton then
begin
CloseR := GetCloseRect(Canvas, Tab, ATabRect);
// close button color
if Tab.Selected then
Brush.Color := CloseColorSelected
else
Brush.Color := CloseColor;
Pen.Color := CloseRectColor;
if not Tab.Enabled then
Pen.Color := CloseRectColorDisabled;
if Tab.Closing then
// shrink
Rectangle(CloseR.Left + 1, CloseR.Top + 1, CloseR.Right - 1, CloseR.Bottom - 1)
else
Rectangle(CloseR);
if Tab.Modified then
Pen.Color := ModifiedCrossColor
else
if Tab.Selected and not Tab.Closing then
Pen.Color := CloseCrossColorSelected
else
if Tab.Enabled then
Pen.Color := CloseCrossColor
else
Pen.Color := CloseCrossColorDisabled;
Pen.Width := 2;
{ Draw close cross }
margin := Scale96(CROSS_MARGIN);
Line(CloseR.Left + margin, CloseR.Top + margin, CloseR.Right - margin - 1, CloseR.Bottom - margin - 1);
Line(CloseR.Left + margin, CloseR.Bottom - margin - 1, CloseR.Right - margin - 1, CloseR.Top + margin);
{
// remove intersection
if Tab.Modified then
FillRect(Rect(CloseR.Left + 5, CloseR.Top + 4, CloseR.Right - 5, CloseR.Bottom - 4));
}
R.Right := CloseR.Left - Scale96(TEXT_MARGIN_RIGHT);
end;
{ Draw image from image list }
if (Tab.ImageIndex <> -1) and (Tab.GetImages <> nil) then
begin
imgsize := GetRealImageSize(Tab);
x := R.Left;
y := (R.Top + R.Bottom - imgSize.CY) div 2;
{$IF LCL_FullVersion >= 1090000}
tabBar := GetTabBar(Tab);
f := tabBar.GetCanvasScalefactor;
ppi := GetPixelsPerInch;
if Tab.GetImages <> nil then
imageRes := Tab.GetImages.ResolutionForPPI[tabBar.ImagesWidth, ppi, f];
imageRes.Draw(Canvas, x, y, Tab.ImageIndex, tab.Enabled);
{$ELSE}
Tab.GetImages.Draw(Canvas, x, y, Tab.ImageIndex, Tab.Enabled);
{$ENDIF}
Inc(R.Left, imgSize.CX + Scale96(TEXT_MARGIN_LEFT));
end;
if Tab.Enabled then
begin
if Tab.Selected then
Font.Assign(Self.SelectedFont)
else
Font.Assign(Self.Font);
end
else
Font.Assign(Self.DisabledFont);
Brush.Style := bsClear;
ts := TextStyle;
ts.EndEllipsis := true;
ts.Clipping := true;
TextRect(R, R.Left, (R.Top + R.Bottom - TextHeight('Tg')) div 2, Tab.Caption, ts);
end;
end;
function TJvModernTabBarPainter.GetCloseRect(Canvas: TCanvas; Tab: TJvTabBarItem;
ATabRect: TRect): TRect;
var
btnSize: TSize;
begin
btnSize := Size(Scale96(CLOSE_BUTTON_SIZE), Scale96(CLOSE_BUTTON_SIZE));
Result.Right := ATabRect.Right - Scale96(RIGHT_MARGIN);
Result.Left := Result.Right - btnSize.CX;
Result.Top := (ATabRect.Top + ATabRect.Bottom - btnSize.CY) div 2;
Result.Bottom := Result.Top + btnSize.CY;
end;
function TJvModernTabBarPainter.GetDividerWidth(Canvas: TCanvas; LeftTab: TJvTabBarItem): Integer;
begin
Result := 1;
end;
function TJvModernTabBarPainter.GetPixelsPerInch: Integer;
begin
Result := Font.PixelsPerInch;
end;
function TJvModernTabBarPainter.GetTabSize(Canvas: TCanvas; Tab: TJvTabBarItem): TSize;
var
w, h: Integer;
imgSize: TSize;
begin
if Tab.Enabled then
begin
if Tab.Selected then
Canvas.Font.Assign(SelectedFont)
else
Canvas.Font.Assign(Font)
end
else
Canvas.Font.Assign(DisabledFont);
// Measure text
if Tab.Caption = '' then
Result := Size(0, Canvas.TextHeight('Tg'))
else
Result := Canvas.TextExtent(Tab.Caption);
inc(Result.CX, Scale96(LEFT_MARGIN) + Scale96(RIGHT_MARGIN));
// Extend width by close button
if Tab.TabBar.CloseButton then begin
w := Scale96(CLOSE_BUTTON_SIZE);
inc(Result.CX, w + Scale96(TEXT_MARGIN_RIGHT));
h := w;
if Result.CY < h then
Result.CY := h;
end;
// Extend width and height by image
if (Tab.ImageIndex <> -1) and (Tab.GetImages <> nil) then begin
imgSize := GetRealImageSize(Tab);
inc(Result.CX, imgSize.CX + Scale96(TEXT_MARGIN_LEFT));
if Result.CY < imgSize.CY then
Result.CY := imgSize.CY;
end;
inc(Result.CY, Scale96(TOP_MARGIN) + Scale96(BOTTOM_MARGIN));
// Override width if TabWidth/TabHeight is fixed.
if TabWidth > 0 then
Result.CX := TabWidth;
if TabHeight > 0 then
Result.CY := TabHeight;
end;
function TJvModernTabBarPainter.Options: TJvTabBarPainterOptions;
begin
Result := [poPaintsHotTab];
end;
procedure TJvModernTabBarPainter.FontChanged(Sender: TObject);
begin
Changed;
end;
procedure TJvModernTabBarPainter.SetBorderColor(const Value: TColor);
begin
if Value <> FBorderColor then
begin
FBorderColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetColor(const Value: TColor);
begin
if Value <> FColor then
begin
FColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetControlDivideColor(const Value: TColor);
begin
if Value <> FControlDivideColor then
begin
FControlDivideColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetModifiedCrossColor(const Value: TColor);
begin
if Value <> FModifiedCrossColor then
begin
FModifiedCrossColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetTabColor(const Value: TColor);
begin
if Value <> FTabColor then
begin
FTabColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseColor(const Value: TColor);
begin
if Value <> FCloseColor then
begin
FCloseColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseColorSelected(const Value: TColor);
begin
if Value <> FCloseColorSelected then
begin
FCloseColorSelected := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseCrossColor(const Value: TColor);
begin
if Value <> FCloseCrossColor then
begin
FCloseCrossColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseCrossColorDisabled(const Value: TColor);
begin
if Value <> FCloseCrossColorDisabled then
begin
FCloseCrossColorDisabled := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseCrossColorSelected(const Value: TColor);
begin
if Value <> FCloseCrossColorSelected then
begin
FCloseCrossColorSelected := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseRectColor(const Value: TColor);
begin
if Value <> FCloseRectColor then
begin
FCloseRectColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetCloseRectColorDisabled(const Value: TColor);
begin
if Value <> FCloseRectColorDisabled then
begin
FCloseRectColorDisabled := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetDividerColor(const Value: TColor);
begin
if Value <> FDividerColor then
begin
FDividerColor := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetTabHeight(Value: Integer);
begin
if Value < 0 then
Value := 0;
if Value <> FTabHeight then
begin
FTabHeight := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetTabWidth(Value: Integer);
begin
if Value < 0 then
Value := 0;
if Value <> FTabWidth then
begin
FTabWidth := Value;
Changed;
end;
end;
procedure TJvModernTabBarPainter.SetFont(const Value: TFont);
begin
if Value <> FFont then
FFont.Assign(Value);
end;
procedure TJvModernTabBarPainter.SetDisabledFont(const Value: TFont);
begin
if Value <> FDisabledFont then
FDisabledFont.Assign(Value);
end;
procedure TJvModernTabBarPainter.SetSelectedFont(const Value: TFont);
begin
if Value <> FSelectedFont then
FSelectedFont.Assign(Value);
end;
end.