1960 lines
57 KiB
ObjectPascal

unit SpkToolbar;
{$mode delphi}
{.$DEFINE EnhancedRecordSupport}
{.$DEFINE DELAYRUNTIMER}
//Translation from Polish into English by Raf20076, Poland, 2016
//I do my best but if you find any mistakes in English comments
//please correct it.
(*******************************************************************************
* *
* File: SpkToolbar.pas *
* Description: Main toolbar component *
* Copyright: (c) 2009 by Spook. *
* License: Modified LGPL (with linking exception, like Lazarus LCL) *
' See "license.txt" in this installation *
* *
* *
*******************************************************************************)
interface
uses
LCLType, LMessages, LCLVersion, Graphics, SysUtils, Controls, Classes, Math,
Dialogs, Forms, Types, ExtCtrls,
SpkGraphTools, SpkGUITools, SpkMath, spkt_Appearance, spkt_BaseItem,
spkt_Const, spkt_Dispatch, spkt_Tab, spkt_Pane, spkt_Types;
type
{ Type describes regions of the toolbar which are used during handling
of interaction with the mouse }
TSpkMouseToolbarElement = (teNone, teToolbarArea, teTabs, teTabContents);
TSpkTabChangingEvent = procedure(Sender: TObject; OldIndex, NewIndex: integer;
var Allowed: boolean) of object;
type
TSpkToolbar = class;
{ Dispatcher class which is used for safe accepting of information
and requests from sub-elements. }
TSpkToolbarDispatch = class(TSpkBaseToolbarDispatch)
private
{ Toolbar component which is accepting information and
requests from sub-elements }
FToolbar: TSpkToolbar;
protected
public
// *******************
// *** Constructor ***
// *******************
//Constructor
constructor Create(AToolbar: TSpkToolbar);
// ******************************************************************
// *** Implementation of abstract methods TSpkBaseToolbarDispatch ***
// ******************************************************************
{ Method (NotifyAppearanceChanged) called when a content of the
object of the appearance changes
The object of the appearance contains colours and fonts used
to draw the toolbar }
procedure NotifyAppearanceChanged; override;
{ Method (NotifyItemsChanged) called when list of the sub-elements
of one of toolbar elements changes }
procedure NotifyItemsChanged; override;
{ Method (NotifyMetricsChanged) called when the size and position (metric)
of one of toolbar elements change }
procedure NotifyMetricsChanged; override;
{ Method (NotifyVisualsChanged) called when the appearance of one of
toolbar elements changes
if the toolbar element however doesn't need rebuilding of metrics }
procedure NotifyVisualsChanged; override;
{ Method (GetTempBitmap) requests for suppporting bitmap delivered by toolbar
For example, used to calculate the size of rendered text }
function GetTempBitmap: TBitmap; override;
{ Method (ClientToScreen) converts the toolbar coordinates to screen coordinates
For example, used to unfold popup menu }
function ClientToScreen(Point: T2DIntPoint): T2DIntPoint; override;
end;
//Extended toolbar inspired by Microsoft Fluent UI
{ TSpkToolbar }
TSpkToolbar = class(TCustomControl)
private
{ Instance of dispatcher object
Dispatcher is transfered to toolbar elements }
FToolbarDispatch: TSpkToolbarDispatch;
{ Buffer bitmap to which toolbar is drawn }
FBuffer: TBitmap;
{ Supporting bitmap is sent when toolbar elements request it }
FTemporary: TBitmap;
{$IFDEF DELAYRUNTIMER}
FDelayRunTimer: TTimer;
{$ENDIF}
{ Array of Rects of "handles" of tabs }
FTabRects: array of T2DIntRect;
{ Cliprect region of "handles" of tabs }
FTabClipRect: T2DIntRect;
{ ClipRect of region content of tab }
FTabContentsClipRect: T2DIntRect;
{ The element over which the mouse pointer is }
FMouseHoverElement: TSpkMouseToolbarElement;
{ The element over which the mouse pointer is and in which a mouse
button is pressed }
FMouseActiveElement: TSpkMouseToolbarElement;
{ The mouse pointer is now on the "handle" of tab }
FTabHover: integer;
{ Flag which informs about validity of metrics of toolbar and its elements }
FMetricsValid: boolean;
{ Flag which informs about validity of buffer content }
FBufferValid: boolean;
{ Flag FInternalUpdating allows to block the validation of metrics and buffer
when component is rebuilding its content
The flag is switched on and off internally by component }
FInternalUpdating: boolean;
{ Flag FUpdating allows to block the validation of metrics and buffer
when user is rebuilding content of the component.
FUpdating is controlled by user }
FUpdating: boolean;
{ Quick selection of different appearances }
FStyle: TSpkStyle;
FOnTabChanging: TSpkTabChangingEvent;
FOnTabChanged: TNotifyEvent;
{$IFDEF DELAYRUNTIMER}
procedure DelayRunTimer(Sender: TObject);
{$ENDIF}
protected
{ Instance of the Appearance object storing colours and fonts used during
rendering of the component }
FAppearance: TSpkToolbarAppearance;
{ Tabs of the toolbar }
FTabs: TSpkTabs;
{ Index of the selected tab }
FTabIndex: integer;
{ Imagelist of the small pictures of toolbar elements }
FImages: TImageList;
{ Image list of the small pictures in the state "disabled".
If the list is not assigned, small "disabled" pictures will be generated
automatically }
FDisabledImages: TImageList;
{ Imagelist of the large pictures of toolbar elements }
FLargeImages: TImageList;
{ Image list of the large pictures in the state "disabled".
If the list is not assigned, large "disabled" pictures will be generated
automatically }
FDisabledLargeImages: TImageList;
{ Unscaled width of the small images }
FImagesWidth: Integer;
{ Unscaled width of the large images }
FLargeImagesWidth: Integer;
function DoTabChanging(OldIndex, NewIndex: integer): boolean;
// *****************************************************
// *** Management of the metric and the buffer state ***
// *****************************************************
{ Method switches flags FMetricsValid and FBufferValid off }
procedure SetMetricsInvalid;
{ Method swiches flag FBufferValid off }
procedure SetBufferInvalid;
{ Method validates toolbar metrics and toolbar elements }
procedure ValidateMetrics;
{ Method validates the content of the buffer }
procedure ValidateBuffer;
{ Method switches on the mode of internal rebuilding
and swiches flag FInternalUpdating on }
procedure InternalBeginUpdate;
{ Method switches on the mode of internal rebuilding
and swiches the flag FInternalUpdating off}
procedure InternalEndUpdate;
// ************************************************
// *** Covering of methods from derived classes ***
// ************************************************
{ The Change of component size }
procedure DoOnResize; override;
{ Method called when mouse pointer left component region }
procedure MouseLeave; override;
{ Method called when mouse button is pressed }
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: integer); override;
{ Method called when mouse pointer is moved over component }
procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
{ Method called when the mouse button is released }
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: integer); override;
{ Method called when the whole component has finished loading from LFM file }
procedure Loaded; override;
{ Method called when component becomes the owner of other component,
or one of its sub-components is released }
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
// ******************************************
// *** Handling of mouse events for tabs ***
// ******************************************
{ Method called when mouse pointer left the region of tab "handles" }
procedure TabMouseLeave;
{ Method called when the mouse button is pressed
and at the same time the mouse pointer is over the region of tabs }
procedure TabMouseDown(Button: TMouseButton; {%H-}Shift: TShiftState;
X, Y: integer);
{ Method called when the mouse will move over the region of tab "handles" }
procedure TabMouseMove({%H-}Shift: TShiftState; X, Y: integer);
{ Method called when one of the mouse buttons is released
and at the same time the region of tabs was active element of toolbar }
procedure TabMouseUp({%H-}Button: TMouseButton; {%H-}Shift: TShiftState;
{%H-}X, {%H-}Y: integer);
// *********************
// *** Extra support ***
// *********************
{ Metchod checks if at least one of the tabs is switched on by flag Visible }
function AtLeastOneTabVisible: boolean;
// ****************
// *** Messages ***
// ****************
{ Message is received when mouse left the region of component }
procedure CMMouseLeave(var msg: TLMessage); message CM_MOUSELEAVE;
// **************************
// *** Designtime and LFM ***
// **************************
{Method gives back elements which will be saved as sub-elements of component }
procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
{ Method allows for saving and reading additional properties of component }
procedure DefineProperties(Filer: TFiler); override;
// ***************************
// *** Getters and setters ***
// ***************************
{ Setter for property Appearance }
procedure SetAppearance(const Value: TSpkToolbarAppearance);
{ Getter for property Color }
function GetColor: TColor;
{ Setter for property Color }
procedure {%H-}SetColor(Value: TColor); // "override" will overflow the stack --> {%H-}
{ Setter for property TabIndex }
procedure SetTabIndex(const Value: integer);
{ Setter for property Images }
procedure SetImages(const Value: TImageList);
{ Setter for property DisabledImages }
procedure SetDisabledImages(const Value: TImageList);
{ Setter for property LargeImages }
procedure SetLargeImages(const Value: TImageList);
{ Setter for property DisabledLargeImages }
procedure SetDisabledLargeImages(const Value: TImageList);
{ Setter for toolbar style, i.e. quick selection of new appearance theme }
procedure SetStyle(const Value: TSpkStyle);
{ LCL Scaling }
{$IF lcl_fullversion >= 1080000}
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
{$IF lcl_fullversion < 1080100}
procedure FixDesignFontsPPI(const ADesignTimePPI: Integer);
procedure ScaleFontsPPI(const AProportion: Double); override;
{$ELSE}
procedure ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); override;
{$ENDIF}
{$ENDIF}
{ Hi-DPI image list support }
procedure SetImagesWidth(const AValue: Integer);
procedure SetLargeImagesWidth(const AValue: Integer);
public
// **********************************
// *** Constructor and Destructor ***
// **********************************
{ Constructor }
constructor Create(AOwner: TComponent); override;
{ Destructor }
destructor Destroy; override;
// *************************
// *** Dispatcher events ***
// *************************
{ Reaction to change of toolbar elements structure }
procedure NotifyItemsChanged;
{ Reaction to change of toolbar elements metric }
procedure NotifyMetricsChanged;
{ Reaction to change of toolbar elements appearance }
procedure NotifyVisualsChanged;
{ Reaction to change of content of toolbar class appearance }
procedure NotifyAppearanceChanged;
{ Method gives back the instance of supporting bitmap }
function GetTempBitmap: TBitmap;
// ***************
// *** Drawing ***
// ***************
// procedure EraseBackground(DC: HDC); override;
{ Method draws the content of the component }
procedure Paint; override;
{ Method enforces the rebuilding of metrics and buffer }
procedure ForceRepaint;
{ Method swiches over the component in update mode of the content
by switching on flag FUpdating }
procedure BeginUpdate;
{ Method switches off the update mode of the content
by switching off flag FUpdating }
procedure EndUpdate;
// ****************
// *** Elements ***
// ****************
{ Method called when one of the tabs is released
You cannot call method FreeingTab from code (by writing it in code)
It's called internally and its purpuse is to update internal list of tabs }
procedure FreeingTab(ATab: TSpkTab);
// **********************
// *** Access to tabs ***
// **********************
{ Property gives accesss to tabs in runtime mode
To edit tabs in designtime mode use proper editor
Savings and readings from LFM is done manually }
property Tabs: TSpkTabs read FTabs;
published
{ Component background color }
property Color: TColor read GetColor write SetColor default clSkyBlue;
{ Appearance style - don't move after Appearance! }
property Style: TSpkStyle read FStyle write SetStyle default spkOffice2007Blue;
{ Object containing attributes of toolbar appearance }
property Appearance: TSpkToolbarAppearance read FAppearance write SetAppearance;
{ Index of active tab }
property TabIndex: integer read FTabIndex write SetTabIndex;
{ ImageList with the small pictures }
property Images: TImageList read FImages write SetImages;
{ ImageList with the small pictures in state "disabled" }
property DisabledImages: TImageList read FDisabledImages write SetDisabledImages;
{ ImageList with the large pictures }
property LargeImages: TImageList read FLargeImages write SetLargeImages;
{ ImageList with the large pictures in state "disabled" }
property DisabledLargeImages: TImageList
read FDisabledLargeImages write SetDisabledLargeImages;
{ Unscaled size of the small images }
property ImagesWidth: Integer read FImagesWidth write SetImagesWidth default 16;
{ Unscaled size of the large images }
property LargeImagesWidth: Integer read FLargeImagesWidth write SetLargeImagesWidth default 32;
{ Events called before and after another tab is selected }
property OnTabChanging: TSpkTabChangingEvent
read FOnTabChanging write FOnTabChanging;
property OnTabChanged: TNotifyEvent read FOnTabChanged write FOnTabChanged;
{ inherited properties }
property Align default alTop;
property BorderSpacing;
property Anchors;
property Hint;
property ParentShowHint;
property ShowHint;
property Visible;
property OnResize;
end;
implementation
uses
LCLIntf, Themes;
{ TSpkToolbarDispatch }
function TSpkToolbarDispatch.ClientToScreen(Point: T2DIntPoint): T2DIntPoint;
begin
{$IFDEF EnhancedRecordSupport}
if FToolbar <> nil then
Result := FToolbar.ClientToScreen(Point)
else
Result := T2DIntPoint.Create(-1, -1);
{$ELSE}
if FToolbar <> nil then
Result := FToolbar.ClientToScreen(Point)
else
Result.Create(-1, -1);
{$ENDIF}
end;
constructor TSpkToolbarDispatch.Create(AToolbar: TSpkToolbar);
begin
inherited Create;
FToolbar := AToolbar;
end;
function TSpkToolbarDispatch.GetTempBitmap: TBitmap;
begin
if FToolbar <> nil then
Result := FToolbar.GetTempBitmap
else
Result := nil;
end;
procedure TSpkToolbarDispatch.NotifyAppearanceChanged;
begin
if FToolbar <> nil then
FToolbar.NotifyAppearanceChanged;
end;
procedure TSpkToolbarDispatch.NotifyMetricsChanged;
begin
if FToolbar <> nil then
FToolbar.NotifyMetricsChanged;
end;
procedure TSpkToolbarDispatch.NotifyItemsChanged;
begin
if FToolbar <> nil then
FToolbar.NotifyItemsChanged;
end;
procedure TSpkToolbarDispatch.NotifyVisualsChanged;
begin
if FToolbar <> nil then
FToolbar.NotifyVisualsChanged;
end;
{ TSpkToolbar }
function TSpkToolbar.AtLeastOneTabVisible: boolean;
var
i: integer;
TabVisible: boolean;
begin
Result := FTabs.Count > 0;
if Result then
begin
TabVisible := False;
i := FTabs.Count - 1;
while (i >= 0) and not TabVisible do
begin
TabVisible := FTabs[i].Visible;
Dec(i);
end;
Result := Result and TabVisible;
end;
end;
procedure TSpkToolbar.BeginUpdate;
begin
FUpdating := True;
end;
procedure TSpkToolbar.CMMouseLeave(var msg: TLMessage);
begin
inherited;
MouseLeave;
end;
constructor TSpkToolbar.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FImagesWidth := 16;
FLargeImagesWidth := 32;
// Initialization of inherited property
Align := alTop;
//todo: not found in lcl
//inherited AlignWithMargins:=true;
if (AOwner is TForm) then
SpkInitLayoutConsts(96); // This default dpi value is ignored for LCL scaling
Height := ToolbarHeight;
//inherited Doublebuffered:=true;
// Initialization of internal data fields
FToolbarDispatch := TSpkToolbarDispatch.Create(self);
FBuffer := TBitmap.Create;
FBuffer.PixelFormat := pf24bit;
FTemporary := TBitmap.Create;
FTemporary.Pixelformat := pf24bit;
SetLength(FTabRects, 0);
{$IFDEF EnhancedRecordSupport}
FTabClipRect := T2DIntRect.Create(0, 0, 0, 0);
FTabContentsClipRect := T2DIntRect.Create(0, 0, 0, 0);
{$ELSE}
FTabClipRect.Create(0, 0, 0, 0);
FTabContentsClipRect.Create(0, 0, 0, 0);
{$ENDIF}
FMouseHoverElement := teNone;
FMouseActiveElement := teNone;
FTabHover := -1;
// Initialization of fields
FAppearance := TSpkToolbarAppearance.Create(FToolbarDispatch);
FTabs := TSpkTabs.Create(self);
FTabs.ToolbarDispatch := FToolbarDispatch;
FTabs.Appearance := FAppearance;
FTabs.ImagesWidth := FImagesWidth;
FTabs.LargeImagesWidth := FLargeImagesWidth;
FTabIndex := -1;
Color := clSkyBlue;
{$IFDEF DELAYRUNTIMER}
FDelayRunTimer := TTimer.Create(nil);
FDelayRunTimer.Interval := 36;
FDelayRunTimer.Enabled := False;
FDelayRunTimer.OnTimer := DelayRunTimer
{$ENDIF}
end;
{$IFDEF DELAYRUNTIMER}
procedure TSpkToolbar.DelayRunTimer(Sender: TObject);
begin
SetMetricsInvalid;
SetBufferInvalid;
invalidate;
FDelayRunTimer.Enabled := False;
end;
{$ENDIF}
procedure TSpkToolbar.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineProperty('Tabs', FTabs.ReadNames, FTabs.WriteNames, True);
end;
destructor TSpkToolbar.Destroy;
begin
// Release the fields
FTabs.Free;
FAppearance.Free;
// Release the internal fields
FTemporary.Free;
FBuffer.Free;
FToolbarDispatch.Free;
{$IFDEF DELAYRUNTIMER}
FDelayRunTimer.Free;
{$ENDIF}
inherited Destroy;
end;
procedure TSpkToolbar.EndUpdate;
begin
FUpdating := False;
ValidateMetrics;
ValidateBuffer;
Repaint;
end;
procedure TSpkToolbar.ForceRepaint;
begin
SetMetricsInvalid;
SetBufferInvalid;
Repaint;
end;
procedure TSpkToolbar.FreeingTab(ATab: TSpkTab);
begin
FTabs.RemoveReference(ATab);
end;
procedure TSpkToolbar.GetChildren(Proc: TGetChildProc; Root: TComponent);
var
i: integer;
begin
inherited;
for i := 0 to FTabs.Count - 1 do
Proc(FTabs.Items[i]);
end;
function TSpkToolbar.GetColor: TColor;
begin
Result := inherited Color;
end;
function TSpkToolbar.GetTempBitmap: TBitmap;
begin
Result := FTemporary;
end;
procedure TSpkToolbar.InternalBeginUpdate;
begin
FInternalUpdating := True;
end;
procedure TSpkToolbar.InternalEndUpdate;
begin
FInternalUpdating := False;
//After internal changes the metrics and buffers are refreshed
ValidateMetrics;
ValidateBuffer;
Repaint;
end;
procedure TSpkToolbar.Loaded;
{$IF LCL_FULLVERSION = 1090000}
const
SM_REMOTESESSION = $1000;
// is defined only after Lazarus r57304
{$ENDIF}
begin
inherited;
{$IF LCL_FULLVERSION >= 1090000}
// Needed due to changes of doublebuffering in Laz r57267
// force DoubleBuffered if not used in remote session
if not (csDesigning in ComponentState) then
DoubleBuffered := DoubleBuffered or (GetSystemMetrics(SM_REMOTESESSION)=0);
{$ENDIF}
InternalBeginUpdate;
if FTabs.ListState = lsNeedsProcessing then
FTabs.ProcessNames(self.Owner);
InternalEndUpdate;
//The process of internal update always refreshes metrics and buffer at the end
//and draws component
end;
procedure TSpkToolbar.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: integer);
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
inherited MouseDown(Button, Shift, X, Y);
//It is possible that the other mouse button was pressed
//In this situation active object receives next notification
if FMouseActiveElement = teTabs then
begin
TabMouseDown(Button, Shift, X, Y);
end
else
if FMouseActiveElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseDown(Button, Shift, X, Y);
end
else
if FMouseActiveElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end
else
//If there is no active element, the active element will be one
//which is now under the mouse
if FMouseActiveElement = teNone then
begin
if FMouseHoverElement = teTabs then
begin
FMouseActiveElement := teTabs;
TabMouseDown(Button, Shift, X, Y);
end
else
if FMouseHoverElement = teTabContents then
begin
FMouseActiveElement := teTabContents;
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseDown(Button, Shift, X, Y);
end
else
if FMouseHoverElement = teToolbarArea then
begin
FMouseActiveElement := teToolbarArea;
//Placeholder if there will be need to use this event
end;
end;
end;
procedure TSpkToolbar.MouseLeave;
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
//MouseLeave has no chance to be called for active object
//because when the mouse button is pressed every mouse move is transfered
//as MouseMove. If the mouse left from component region then
//MouseLeave will be called just after MouseUp but MouseUp cleans the
//active object
if FMouseActiveElement = teNone then
begin
//If there is no active element, the elements under mouse will be supported
if FMouseHoverElement = teTabs then
begin
TabMouseLeave;
end
else
if FMouseHoverElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseLeave;
end
else
if FMouseHoverElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end;
end;
FMouseHoverElement := teNone;
end;
procedure TSpkToolbar.MouseMove(Shift: TShiftState; X, Y: integer);
var
NewMouseHoverElement: TSpkMouseToolbarElement;
MousePoint: T2DIntVector;
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
inherited MouseMove(Shift, X, Y);
//Checking which element is under the mouse
{$IFDEF EnhancedRecordSupport}
MousePoint := T2DIntVector.Create(x, y);
{$ELSE}
MousePoint.Create(x, y);
{$ENDIF}
if FTabClipRect.Contains(MousePoint) then
NewMouseHoverElement := teTabs
else
if FTabContentsClipRect.Contains(MousePoint) then
NewMouseHoverElement := teTabContents
else
if (X >= 0) and (Y >= 0) and (X < self.Width) and (Y < self.Height) then
NewMouseHoverElement := teToolbarArea
else
NewMouseHoverElement := teNone;
//If there is an active element then it has exlusiveness for messages
if FMouseActiveElement = teTabs then
begin
TabMouseMove(Shift, X, Y);
end
else
if FMouseActiveElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseMove(Shift, X, Y);
end
else
if FMouseActiveElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end
else
if FMouseActiveElement = teNone then
begin
//If element changes under the mouse, then previous element will be informed
//that mouse is leaving its region
if NewMouseHoverElement <> FMouseHoverElement then
begin
if FMouseHoverElement = teTabs then
begin
TabMouseLeave;
end
else
if FMouseHoverElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseLeave;
end
else
if FMouseHoverElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end;
end;
//Element under mouse receives MouseMove
if NewMouseHoverElement = teTabs then
begin
TabMouseMove(Shift, X, Y);
end
else
if NewMouseHoverElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseMove(Shift, X, Y);
end
else
if NewMouseHoverElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end;
end;
FMouseHoverElement := NewMouseHoverElement;
end;
procedure TSpkToolbar.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer);
var
ClearActive: boolean;
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
inherited MouseUp(Button, Shift, X, Y);
ClearActive := not (ssLeft in Shift) and not (ssMiddle in Shift) and not (ssRight in Shift);
//If there is an active element then it has exlusiveness for messages
if FMouseActiveElement = teTabs then
begin
TabMouseUp(Button, Shift, X, Y);
end
else
if FMouseActiveElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseUp(Button, Shift, X, Y);
end
else
if FMouseActiveElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end;
//If the last mouse button is released and mouse doesn't locate over
//the active object, it must additionally call MouseLeave for active one
//and MouseMove for object being under mouse
if ClearActive and (FMouseActiveElement <> FMouseHoverElement) then
begin
if FMouseActiveElement = teTabs then
TabMouseLeave
else
if FMouseActiveElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseLeave;
end
else
if FMouseActiveElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end;
if FMouseHoverElement = teTabs then
TabMouseMove(Shift, X, Y)
else
if FMouseHoverElement = teTabContents then
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].MouseMove(Shift, X, Y);
end
else
if FMouseHoverElement = teToolbarArea then
begin
//Placeholder if there will be need to use this event
end;
end;
//MouseUp swiches off active object, when all mouse buttons were released
if ClearActive then
FMouseActiveElement := teNone;
end;
procedure TSpkToolbar.Notification(AComponent: TComponent; Operation: TOperation);
var
Tab: TSpkTab;
Pane: TSpkPane;
Item: TSpkBaseItem;
begin
inherited;
if Operation <> opRemove then
exit;
if AComponent is TSpkTab then
begin
FreeingTab(AComponent as TSpkTab);
end
else
if AComponent is TSpkPane then
begin
Pane := AComponent as TSpkPane;
if (Pane.Parent <> nil) and (Pane.Parent is TSpkTab) then
begin
Tab := Pane.Parent as TSpkTab;
Tab.FreeingPane(Pane);
end;
end
else
if AComponent is TSpkBaseItem then
begin
Item := AComponent as TSpkBaseItem;
if (Item.Parent <> nil) and (Item.Parent is TSpkPane) then
begin
Pane := Item.Parent as TSpkPane;
Pane.FreeingItem(Item);
end;
end;
end;
procedure TSpkToolbar.NotifyAppearanceChanged;
begin
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.NotifyMetricsChanged;
begin
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.NotifyItemsChanged;
var
OldTabIndex: integer;
begin
OldTabIndex := FTabIndex;
// Fixed TabIndex when you need it
if not (AtLeastOneTabVisible) then
FTabIndex := -1
else
begin
FTabIndex := max(0, min(FTabs.Count - 1, FTabIndex));
//I know that at least one tab is visible (from previous condition)
//so below loop will finish
while not (FTabs[FTabIndex].Visible) do
FTabIndex := (FTabIndex + 1) mod FTabs.Count;
end;
FTabHover := -1;
if DoTabChanging(OldTabIndex, FTabIndex) then
begin
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
if Assigned(FOnTabChanged) then
FOnTabChanged(self);
end
else
FTabIndex := OldTabIndex;
end;
procedure TSpkToolbar.NotifyVisualsChanged;
begin
SetBufferInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.Paint;
begin
//If the rebuilding process (internal or by user) is running now
//then validation of metrics and buffer is not running, however
//the buffer is drawn in a shape what was remembered before rebuilding process
if not (FInternalUpdating or FUpdating) then
begin
if not (FMetricsValid) then
ValidateMetrics;
if not (FBufferValid) then
ValidateBuffer;
end;
self.canvas.draw(0, 0, FBuffer);
end;
procedure TSpkToolbar.DoOnResize;
begin
if Height <> ToolbarHeight then
Height := ToolbarHeight;
{$IFDEF DELAYRUNTIMER}
FDelayRunTimer.Enabled := False;
FDelayRunTimer.Enabled := True;
{$ELSE}
SetMetricsInvalid;
SetBufferInvalid;
{$ENDIF}
if not (FInternalUpdating or FUpdating) then
invalidate;
inherited;
end;
(*
procedure TSpkToolbar.EraseBackground(DC: HDC);
begin
// The correct implementation is doing nothing
if ThemeServices.ThemesEnabled then
inherited; // wp: this calls FillRect!
// "inherited" removed in case of no themes to fix issue #0025047 (flickering
// when using standard windows theme or when manifest file is off)
end; *)
procedure TSpkToolbar.SetBufferInvalid;
begin
FBufferValid := False;
end;
procedure TSpkToolbar.SetColor(Value: TColor);
begin
inherited Color := Value;
SetBufferInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.SetDisabledImages(const Value: TImageList);
begin
FDisabledImages := Value;
FTabs.DisabledImages := Value;
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.SetDisabledLargeImages(const Value: TImageList);
begin
FDisabledLargeImages := Value;
FTabs.DisabledLargeImages := Value;
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.SetImages(const Value: TImageList);
begin
FImages := Value;
FTabs.Images := Value;
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.SetLargeImages(const Value: TImageList);
begin
FLargeImages := Value;
FTabs.LargeImages := Value;
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.SetStyle(const Value: TSpkStyle);
begin
FStyle := Value;
FAppearance.Reset(FStyle);
ForceRepaint;
end;
function TSpkToolbar.DoTabChanging(OldIndex, NewIndex: integer): boolean;
begin
Result := True;
if Assigned(FOnTabChanging) then
FOnTabChanging(Self, OldIndex, NewIndex, Result);
end;
procedure TSpkToolbar.SetMetricsInvalid;
begin
FMetricsValid := False;
FBufferValid := False;
end;
procedure TSpkToolbar.SetTabIndex(const Value: integer);
var
OldTabIndex: integer;
begin
OldTabIndex := FTabIndex;
if not (AtLeastOneTabVisible) then
FTabIndex := -1
else
begin
FTabIndex := max(0, min(FTabs.Count - 1, Value));
//I know that at least one tab is visible (from previous condition)
//so below loop will finish
while not (FTabs[FTabIndex].Visible) do
FTabIndex := (FTabIndex + 1) mod FTabs.Count;
end;
FTabHover := -1;
if DoTabChanging(OldTabIndex, FTabIndex) then
begin
SetMetricsInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
if Assigned(FOnTabChanged) then
FOnTabChanged(self);
end
else
FTabIndex := OldTabIndex;
end;
procedure TSpkToolbar.TabMouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: integer);
var
SelTab: integer;
TabRect: T2DIntRect;
i: integer;
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
SelTab := -1;
if AtLeastOneTabVisible then
for i := 0 to FTabs.Count - 1 do
if FTabs[i].Visible then
begin
if FTabClipRect.IntersectsWith(FTabRects[i], TabRect) then
{$IFDEF EnhancedRecordSupport}
if TabRect.Contains(T2DIntPoint.Create(x, y)) then
{$ELSE}
if TabRect.Contains(x, y) then
{$ENDIF}
SelTab := i;
end;
//If any tab was clicked but one (not being selected) then change selection
if (Button = mbLeft) and (SelTab <> -1) and (SelTab <> FTabIndex) then
begin
if DoTabChanging(FTabIndex, SelTab) then
begin
FTabIndex := SelTab;
SetMetricsInvalid;
Repaint;
if Assigned(FOnTabChanged) then
FOnTabChanged(self);
end;
end;
end;
procedure TSpkToolbar.TabMouseLeave;
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
if FTabHover <> -1 then
begin
FTabHover := -1;
SetBufferInvalid;
Repaint;
end;
end;
procedure TSpkToolbar.TabMouseMove(Shift: TShiftState; X, Y: integer);
var
NewTabHover: integer;
TabRect: T2DIntRect;
i: integer;
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
NewTabHover := -1;
if AtLeastOneTabVisible then
for i := 0 to FTabs.Count - 1 do
if FTabs[i].Visible then
begin
if FTabClipRect.IntersectsWith(FTabRects[i], TabRect) then
{$IFDEF EnhancedRecordSupport}
if TabRect.Contains(T2DIntPoint.Create(x, y)) then
{$ELSE}
if TabRect.Contains(x, y) then
{$ENDIF}
NewTabHover := i;
end;
if NewTabHover <> FTabHover then
begin
FTabHover := NewTabHover;
SetBufferInvalid;
Repaint;
end;
end;
procedure TSpkToolbar.TabMouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: integer);
begin
//During rebuilding procees the mouse is ignored
if FInternalUpdating or FUpdating then
exit;
if (FTabIndex > -1) then
FTabs[FTabIndex].ExecOnClick;
//Tabs don't need MouseUp
end;
procedure TSpkToolbar.SetAppearance(const Value: TSpkToolbarAppearance);
begin
FAppearance.Assign(Value);
SetBufferInvalid;
if not (FInternalUpdating or FUpdating) then
Repaint;
end;
procedure TSpkToolbar.ValidateBuffer;
procedure DrawBackgroundColor;
begin
FBuffer.Canvas.Brush.Color := Color;
FBuffer.Canvas.Brush.Style := bsSolid;
FBuffer.Canvas.FillRect(Rect(0, 0, self.Width, self.Height));
end;
procedure DrawBody;
var
FocusedAppearance: TSpkToolbarAppearance;
i: integer;
begin
//Loading appearance of selected tab
//or FToolbarAppearance if selected tab has no set OverrideAppearance
if (FTabIndex <> -1) and (FTabs[FTabIndex].OverrideAppearance) then
FocusedAppearance := FTabs[FTabIndex].CustomAppearance
else
FocusedAppearance := FAppearance;
TGuiTools.DrawRoundRect(FBuffer.Canvas,
{$IFDEF EnhancedRecordSupport}
T2DIntRect.Create(0, ToolbarTabCaptionsHeight, self.Width - 1, self.Height - 1),
{$ELSE}
Create2DIntRect(0, ToolbarTabCaptionsHeight, self.Width - 1, self.Height - 1),
{$ENDIF}
ToolbarCornerRadius,
FocusedAppearance.Tab.GradientFromColor,
FocusedAppearance.Tab.GradientToColor,
FocusedAppearance.Tab.GradientType);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(0, ToolbarTabCaptionsHeight),
{$ELSE}
Create2DIntPoint(0, ToolbarTabCaptionsHeight),
{$ENDIF}
ToolbarCornerRadius,
cpLeftTop,
FocusedAppearance.Tab.BorderColor);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(self.Width - ToolbarCornerRadius, ToolbarTabCaptionsHeight),
{$ELSE}
Create2DIntPoint(self.Width - ToolbarCornerRadius, ToolbarTabCaptionsHeight),
{$ENDIF}
ToolbarCornerRadius,
cpRightTop,
FocusedAppearance.Tab.BorderColor);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(0, self.Height - ToolbarCornerRadius),
{$ELSE}
Create2DIntPoint(0, self.Height - ToolbarCornerRadius),
{$ENDIF}
ToolbarCornerRadius,
cpLeftBottom,
FocusedAppearance.Tab.BorderColor);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(self.Width - ToolbarCornerRadius, self.Height - ToolbarCornerRadius),
{$ELSE}
Create2DIntPoint(self.Width - ToolbarCornerRadius, self.Height - ToolbarCornerRadius),
{$ENDIF}
ToolbarCornerRadius,
cpRightBottom,
FocusedAppearance.Tab.BorderColor);
TGuiTools.DrawVLine(FBuffer, 0, ToolbarTabCaptionsHeight +
ToolbarCornerRadius, self.Height - ToolbarCornerRadius,
FocusedAppearance.Tab.BorderColor);
TGuiTools.DrawHLine(FBuffer, ToolbarCornerRadius, self.Width - ToolbarCornerRadius,
self.Height - 1, FocusedAppearance.Tab.BorderColor);
TGuiTools.DrawVLine(FBuffer, self.Width - 1, ToolbarTabCaptionsHeight +
ToolbarCornerRadius, self.Height - ToolbarCornerRadius,
FocusedAppearance.Tab.BorderColor);
if not (AtLeastOneTabVisible) then
begin
//If there are no tabs then the horizontal line will be drawn
TGuiTools.DrawHLine(FBuffer, ToolbarCornerRadius, self.Width -
ToolbarCornerRadius, ToolbarTabCaptionsHeight, FocusedAppearance.Tab.BorderColor);
end
else
begin
//If there are tabs then the place will be left for them
//Last visible tab is looked for
i := FTabs.Count - 1;
while not (FTabs[i].Visible) do
Dec(i);
//Only right part, the rest will be drawn with tabs
if FTabRects[i].Right < self.Width - ToolbarCornerRadius - 1 then
TGuiTools.DrawHLine(FBuffer, FTabRects[i].Right + 1, self.Width -
ToolbarCornerRadius, ToolbarTabCaptionsHeight, FocusedAppearance.Tab.BorderColor);
end;
end;
procedure DrawTabs;
var
i: integer;
// TabRect: T2DIntRect;
CurrentAppearance: TSpkToolbarAppearance;
FocusedAppearance: TSpkToolbarAppearance;
procedure DrawTabText(index: integer; AFont: TFont; AOverrideTextColor: TColor = clNone);
var
x, y: integer;
TabRect: T2DIntRect;
clr: TColor;
begin
TabRect := FTabRects[index];
FBuffer.canvas.font.Assign(AFont);
if AOverrideTextColor <> clNone then
clr := AOverrideTextColor else
clr := AFont.Color;
x := TabRect.left + (TabRect.Width - FBuffer.Canvas.textwidth(
FTabs[index].Caption)) div 2;
y := TabRect.top + (TabRect.Height - FBuffer.Canvas.Textheight('Wy')) div 2;
TGuiTools.DrawText(FBuffer.Canvas,
x,
y,
FTabs[index].Caption,
clr,
FTabClipRect);
end;
procedure DrawTab(index: integer;
Border, GradientFrom, GradientTo: TColor);
var
TabRect: T2DIntRect;
TabRegion: HRGN;
TmpRegion, TmpRegion2: HRGN;
begin
//Note!! Tabs cover one pixel of toolbar region, because
// the they must draw edge, which fits in with region edge
TabRect := FTabRects[index];
//Middle rectangle
TabRegion := CreateRectRgn(
TabRect.Left + TabCornerRadius - 1,
TabRect.Top + TabCornerRadius,
TabRect.Right - TabCornerRadius + 1 + 1,
TabRect.Bottom + 1
);
//Top part with top convex curves
TmpRegion := CreateRectRgn(
TabRect.Left + 2 * TabCornerRadius - 1,
TabRect.Top,
TabRect.Right - 2 * TabCornerRadius + 1 + 1,
TabRect.Top + TabCornerRadius
);
CombineRgn(TabRegion, TabRegion, TmpRegion, RGN_OR);
DeleteObject(TmpRegion);
TmpRegion := CreateEllipticRgn(
TabRect.Left + TabCornerRadius - 1,
TabRect.Top,
TabRect.Left + 3 * TabCornerRadius,
TabRect.Top + 2 * TabCornerRadius + 1
);
CombineRgn(TabRegion, TabRegion, TmpRegion, RGN_OR);
DeleteObject(TmpRegion);
TmpRegion := CreateEllipticRgn(
TabRect.Right - 3 * TabCornerRadius + 2,
TabRect.Top,
TabRect.Right - TabCornerRadius + 3,
TabRect.Top + 2 * TabCornerRadius + 1
);
CombineRgn(TabRegion, TabRegion, TmpRegion, RGN_OR);
DeleteObject(TmpRegion);
//Bottom part with bottom convex curves
TmpRegion := CreateRectRgn(
TabRect.Left,
TabRect.Bottom - TabCornerRadius,
TabRect.Right + 1,
TabRect.Bottom + 1
);
TmpRegion2 := CreateEllipticRgn(
TabRect.Left - TabCornerRadius,
TabRect.Bottom - 2 * TabCornerRadius + 1,
TabRect.Left + TabCornerRadius + 1,
TabRect.Bottom + 2
);
CombineRgn(TmpRegion, TmpRegion, TmpRegion2, RGN_DIFF);
DeleteObject(TmpRegion2);
TmpRegion2 := CreateEllipticRgn(
TabRect.Right - TabCornerRadius + 1,
TabRect.Bottom - 2 * TabCornerRadius + 1,
TabRect.Right + TabCornerRadius + 2,
TabRect.Bottom + 2
);
CombineRgn(TmpRegion, TmpRegion, TmpRegion2, RGN_DIFF);
DeleteObject(TmpRegion2);
CombineRgn(TabRegion, TabRegion, TmpRegion, RGN_OR);
DeleteObject(TmpRegion);
TGUITools.DrawRegion(FBuffer.Canvas,
TabRegion,
TabRect,
GradientFrom,
GradientTo,
bkVerticalGradient);
DeleteObject(TabRegion);
// Frame
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(TabRect.left, TabRect.bottom - TabCornerRadius + 1),
{$ELSE}
Create2DIntPoint(TabRect.left, TabRect.bottom - TabCornerRadius + 1),
{$ENDIF}
TabCornerRadius,
cpRightBottom,
Border,
FTabClipRect);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(TabRect.right - TabCornerRadius + 1, TabRect.bottom - TabCornerRadius + 1),
{$ELSE}
Create2DIntPoint(TabRect.right - TabCornerRadius + 1, TabRect.bottom - TabCornerRadius + 1),
{$ENDIF}
TabCornerRadius,
cpLeftBottom,
Border,
FTabClipRect);
TGuiTools.DrawVLine(FBuffer,
TabRect.left + TabCornerRadius - 1,
TabRect.top + TabCornerRadius,
TabRect.Bottom - TabCornerRadius + 1,
Border,
FTabClipRect);
TGuiTools.DrawVLine(FBuffer,
TabRect.Right - TabCornerRadius + 1,
TabRect.top + TabCornerRadius,
TabRect.Bottom - TabCornerRadius + 1,
Border,
FTabClipRect);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(TabRect.Left + TabCornerRadius - 1, 0),
{$ELSE}
Create2DIntPoint(TabRect.Left + TabCornerRadius - 1, 0),
{$ENDIF}
TabCornerRadius,
cpLeftTop,
Border,
FTabClipRect);
TGuiTools.DrawAARoundCorner(FBuffer,
{$IFDEF EnhancedRecordSupport}
T2DIntPoint.Create(TabRect.Right - 2 * TabCornerRadius + 2, 0),
{$ELSE}
Create2DIntPoint(TabRect.Right - 2 * TabCornerRadius + 2, 0),
{$ENDIF}
TabCornerRadius,
cpRightTop,
Border,
FTabClipRect);
TGuiTools.DrawHLine(FBuffer,
TabRect.Left + 2 * TabCornerRadius - 1,
TabRect.Right - 2 * TabCornerRadius + 2,
0,
Border,
FTabClipRect);
end;
procedure DrawBottomLine(index: integer;
Border: TColor);
var
TabRect: T2DIntRect;
begin
TabRect := FTabRects[index];
TGUITools.DrawHLine(FBuffer,
TabRect.left,
TabRect.right,
TabRect.bottom,
Border,
FTabClipRect);
end;
var
delta: Integer;
begin
//I assume that the tabs size is reasonable
//Loading appearance of selected now tab (her appearance, if
//its flag - OverrideAppearance is switched on otherwise
//FToolbarAppearance
if (FTabIndex <> -1) and (FTabs[FTabIndex].OverrideAppearance) then
FocusedAppearance := FTabs[FTabIndex].CustomAppearance
else
FocusedAppearance := FAppearance;
if FTabs.Count > 0 then
for i := 0 to FTabs.Count - 1 do
if FTabs[i].Visible then
begin
// Is there any sense to draw?
if not (FTabClipRect.IntersectsWith(FTabRects[i])) then
continue;
//Loading appearance of now drawn tab
if (FTabs[i].OverrideAppearance) then
CurrentAppearance := FTabs[i].CustomAppearance
else
CurrentAppearance := FAppearance;
if CurrentAppearance.Tab.GradientType = bkSolid then
delta := 0 else
delta := 50;
//TabRect := FTabRects[i];
// Tab is drawn
if i = FTabIndex then // active tab
begin
if i = FTabHover then
begin
DrawTab(i,
CurrentAppearance.Tab.BorderColor,
TColorTools.Brighten(TColorTools.Brighten(
CurrentAppearance.Tab.GradientFromColor, delta), delta),
CurrentAppearance.Tab.GradientFromColor);
end
else
begin
DrawTab(i,
CurrentAppearance.Tab.BorderColor,
TColorTools.Brighten(
CurrentAppearance.Tab.GradientFromColor, delta),
CurrentAppearance.Tab.GradientFromColor);
end;
DrawTabText(i, CurrentAppearance.Tab.TabHeaderFont);
end
else
begin // inactive tab
if i = FTabHover then
begin
DrawTab(i,
TColorTools.Shade(
self.Color, CurrentAppearance.Tab.BorderColor, delta),
TColorTools.Shade(self.color,
TColorTools.Brighten(CurrentAppearance.Tab.GradientFromColor, delta), 50),
TColorTools.Shade(
self.color, CurrentAppearance.Tab.GradientFromColor, 50) );
end;
// Bottom line
//Warning!! Irrespective of tab , the appearance will be drawn
//with color now selected tab
DrawBottomLine(i, FocusedAppearance.Tab.BorderColor);
// Text
DrawTabText(i, CurrentAppearance.Tab.TabHeaderFont,
CurrentAppearance.Tab.InactiveTabHeaderFontColor);
end;
end;
end;
procedure DrawTabContents;
begin
if FTabIndex <> -1 then
FTabs[FTabIndex].Draw(FBuffer, FTabContentsClipRect);
end;
begin
if FInternalUpdating or FUpdating then
exit;
if FBufferValid then
exit;
// ValidateBuffer could be called only when metrics is calulated
//Method assumes that buffer has proper sizes and all rects of toolbar and
//sub-elements are correctly calculated
// *** Component background ***
DrawBackgroundColor;
// *** The toolbar background is generated ***
DrawBody;
// *** Tabs ***
DrawTabs;
// *** Tabs content ***
DrawTabContents;
// Buffer is correct
FBufferValid := True;
end;
procedure TSpkToolbar.ValidateMetrics;
var
i: integer;
x: integer;
TabWidth: integer;
TabAppearance: TSpkToolbarAppearance;
begin
if FInternalUpdating or FUpdating then
exit;
if FMetricsValid then
exit;
FBuffer.Free;
FBuffer := TBitmap.Create;
FBuffer.SetSize(self.Width, self.Height);
// *** Tabs ***
// Cliprect of tabs (containg top frame of component)
{$IFDEF EnhancedRecordSupport}
FTabClipRect := T2DIntRect.Create(
ToolbarCornerRadius,
0,
self.Width - ToolbarCornerRadius - 1,
ToolbarTabCaptionsHeight);
{$ELSE}
FTabClipRect.Create(
ToolbarCornerRadius,
0,
self.Width - ToolbarCornerRadius - 1,
ToolbarTabCaptionsHeight);
{$ENDIF}
// Rects of tabs headings (containg top frame of component)
Setlength(FTabRects, FTabs.Count);
if FTabs.Count > 0 then
begin
x := ToolbarCornerRadius;
for i := 0 to FTabs.Count - 1 do
if FTabs[i].Visible then
begin
// Loading appearance of tab
if FTabs[i].OverrideAppearance then
TabAppearance := FTabs[i].CustomAppearance
else
TabAppearance := FAppearance;
FBuffer.Canvas.Font.Assign(TabAppearance.Tab.TabHeaderFont);
TabWidth := 2 + // Frame
2 * TabCornerRadius +
// Curves
2 * ToolbarTabCaptionsTextHPadding +
// Internal margins
max(ToolbarMinTabCaptionWidth,
FBuffer.Canvas.TextWidth(FTabs.Items[i].Caption));
// Breadth of text
FTabRects[i].Left := x;
FTabRects[i].Right := x + TabWidth - 1;
FTabRects[i].Top := 0;
FTabRects[i].Bottom := ToolbarTabCaptionsHeight;
x := FTabRects[i].right + 1;
end
else
begin
{$IFDEF EnhancedRecordSupport}
FTabRects[i] := T2DIntRect.Create(-1, -1, -1, -1);
{$ELSE}
FTabRects[i].Create(-1, -1, -1, -1);
{$ENDIF}
end;
end;
// *** Panes ***
if FTabIndex <> -1 then
begin
// Rect of tab region
{$IFDEF EnhancedRecordSupport}
FTabContentsClipRect := T2DIntRect.Create(ToolbarBorderWidth + TabPaneLeftPadding,
ToolbarTabCaptionsHeight + ToolbarBorderWidth + TabPaneTopPadding,
self.Width - 1 - ToolbarBorderWidth - TabPaneRightPadding,
self.Height - 1 - ToolbarBorderWidth - TabPaneBottomPadding);
{$ELSE}
FTabContentsClipRect.Create(ToolbarBorderWidth + TabPaneLeftPadding,
ToolbarTabCaptionsHeight + ToolbarBorderWidth + TabPaneTopPadding,
self.Width - 1 - ToolbarBorderWidth - TabPaneRightPadding,
self.Height - 1 - ToolbarBorderWidth - TabPaneBottomPadding);
{$ENDIF}
FTabs[FTabIndex].Rect := FTabContentsClipRect;
end;
FMetricsValid := True;
end;
{$IF lcl_fullversion >= 1080000}
procedure TSpkToolbar.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double);
begin
inherited;
if not (AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI]) then
exit;
LargeButtonDropdownFieldSize := round(LARGEBUTTON_DROPDOWN_FIELD_SIZE * AXProportion);
LargeButtonGlyphMargin := round(LARGEBUTTON_GLYPH_MARGIN * AXProportion);
LargeButtonCaptionHMargin := round(LARGEBUTTON_CAPTION_HMARGIN * AXProportion);
LargeButtonMinWidth := round(LARGEBUTTON_MIN_WIDTH * AXProportion);
LargeButtonRadius := LARGEBUTTON_RADIUS;
LargeButtonBorderSize := round(LARGEBUTTON_BORDER_SIZE * AXProportion);
LargeButtonChevronVMargin := round(LARGEBUTTON_CHEVRON_VMARGIN * AYProportion);
LargeButtonCaptionTopRail := round(LARGEBUTTON_CAPTION_TOP_RAIL * AYProportion);
LargeButtonCaptionButtomRail := round(LARGEBUTTON_CAPTION_BOTTOM_RAIL * AYProportion);
SmallButtonGlyphWidth := round(SMALLBUTTON_GLYPH_WIDTH * AXProportion);
SmallButtonBorderWidth := round(SMALLBUTTON_BORDER_WIDTH * AXProportion);
SmallButtonHalfBorderWidth := round(SMALLBUTTON_HALF_BORDER_WIDTH * AXProportion);
SmallButtonPadding := round(SMALLBUTTON_PADDING * AXProportion);
SmallButtonDropdownWidth := round(SMALLBUTTON_DROPDOWN_WIDTH * AXProportion);
SmallButtonRadius := SMALLBUTTON_RADIUS;
SmallButtonMinWidth := 2 * SmallButtonPadding + SmallButtonGlyphWidth;
MaxElementHeight := round(MAX_ELEMENT_HEIGHT * AYProportion);
PaneRowHeight := round(PANE_ROW_HEIGHT * AYProportion);
PaneFullRowHeight := 3 * PaneRowHeight;
PaneOneRowTopPadding := round(PANE_ONE_ROW_TOPPADDING * AYProportion);
PaneOneRowBottomPadding := round(PANE_ONE_ROW_BOTTOMPADDING * AYProportion);
PaneTwoRowsVSpacer := round(PANE_TWO_ROWS_VSPACER * AYProportion);
PaneTwoRowsTopPadding := round(PANE_TWO_ROWS_TOPPADDING * AYProportion);
PaneTwoRowsBottomPadding := round(PANE_TWO_ROWS_BOTTOMPADDING * AYProportion);
PaneThreeRowsVSpacer := round(PANE_THREE_ROWS_VSPACER * AYProportion);
PaneThreeRowsTopPadding := round(PANE_THREE_ROWS_TOPPADDING * AYProportion);
PaneThreeRowsBottomPadding := round(PANE_THREE_ROWS_BOTTOMPADDING * AYProportion);
PaneFullRowTopPadding := PaneThreeRowsTopPadding;
PaneFullRowBottomPadding := PaneThreeRowsBottomPadding;
PaneLeftPadding := round(PANE_LEFT_PADDING * AXProportion);
PaneRightPadding := round(PANE_RIGHT_PADDING * AXProportion);
PaneColumnSpacer := round(PANE_COLUMN_SPACER * AXProportion);
PaneGroupSpacer := round(PANE_GROUP_SPACER * AXProportion);
PaneCaptionHeight := round(PANE_CAPTION_HEIGHT * AYProportion);
PaneCornerRadius := PANE_CORNER_RADIUS;
PaneBorderSize := round(PANE_BORDER_SIZE * AXProportion);
PaneBorderHalfSize := round(PANE_BORDER_HALF_SIZE * AXProportion);
PaneHeight := MaxElementHeight + PaneCaptionHeight + 2 * PaneBorderSize;
PaneCaptionHMargin := round(PANE_CAPTION_HMARGIN * AXProportion);
TabCornerRadius := TAB_CORNER_RADIUS;
TabPaneLeftPadding := round(TAB_PANE_LEFTPADDING * AXProportion);
TabPaneRightPadding := round(TAB_PANE_RIGHTPADDING * AXProportion);
TabPaneTopPadding := round(TAB_PANE_TOPPADDING * AYProportion);
TabPaneBottomPadding := round(TAB_PANE_BOTTOMPADDING * AYProportion);
TabPaneHSpacing := round(TAB_PANE_HSPACING * AXProportion);
TabBorderSize := round(TAB_BORDER_SIZE * AXProportion);
TabHeight := PaneHeight + TabPaneTopPadding + TabPaneBottomPadding + TabBorderSize;
ToolbarBorderWidth := round(TOOLBAR_BORDER_WIDTH * AXProportion);
ToolbarCornerRadius := TOOLBAR_CORNER_RADIUS;
ToolbarTabCaptionsHeight := round(TOOLBAR_TAB_CAPTIONS_HEIGHT * AYProportion);
ToolbarTabCaptionsTextHPadding := round(TOOLBAR_TAB_CAPTIONS_TEXT_HPADDING * AXProportion);
ToolbarMinTabCaptionWidth := round(TOOLBAR_MIN_TAB_CAPTION_WIDTH * AXProportion);
ToolbarHeight := ToolbarTabCaptionsHeight + TabHeight;
// scaling radius if not square
if LargeButtonRadius > 1 then
LargeButtonRadius := round(LargeButtonRadius * AXProportion);
if SmallButtonRadius > 1 then
SmallButtonRadius := round(SmallButtonRadius * AXProportion);
if PaneCornerRadius > 1 then
PaneCornerRadius := round(PaneCornerRadius * AXProportion);
if TabCornerRadius > 1 then
TabCornerRadius := round(TabCornerRadius * AXProportion);
if ToolbarCornerRadius > 1 then
ToolbarCornerRadius := round(ToolbarCornerRadius * AXProportion);
end;
{$IF lcl_fullversion < 1080100}
procedure TSpkToolbar.FixDesignFontsPPI(const ADesignTimePPI: Integer);
begin
inherited;
DoFixDesignFontPPI(FAppearance.Tab.TabHeaderFont, ADesignTimePPI);
DoFixDesignFontPPI(FAppearance.Pane.CaptionFont, ADesignTimePPI);
DoFixDesignFontPPI(FAppearance.Element.CaptionFont, ADesignTimePPI);
end;
procedure TSpkToolbar.ScaleFontsPPI(const AProportion: Double);
begin
inherited;
DoScaleFontPPI(FAppearance.Tab.TabHeaderFont, AProportion);
DoScaleFontPPI(FAppearance.Pane.CaptionFont, AProportion);
DoScaleFontPPI(FAppearance.Element.CaptionFont, AProportion);
end;
{$ELSE}
procedure TSpkToolbar.ScaleFontsPPI(const AToPPI: Integer;
const AProportion: Double);
begin
inherited;
DoScaleFontPPI(FAppearance.Tab.TabHeaderFont, AToPPI, AProportion);
DoScaleFontPPI(FAppearance.Pane.CaptionFont, AToPPI, AProportion);
DoScaleFontPPI(FAppearance.Element.CaptionFont, AToPPI, AProportion);
end;
{$ENDIF}
{$ENDIF}
{ Hi-DPI image list support }
procedure TSpkToolbar.SetImagesWidth(const AValue: Integer);
begin
if FImagesWidth = AValue then Exit;
FImagesWidth := AValue;
NotifyMetricsChanged
end;
procedure TSpkToolbar.SetLargeImagesWidth(const AValue: Integer);
begin
if FLargeImagesWidth = AValue then Exit;
FLargeImagesWidth := AValue;
NotifyMetricsChanged
end;
end.