mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-30 03:27:44 +00:00
423 lines
11 KiB
ObjectPascal
423 lines
11 KiB
ObjectPascal
(*
|
|
Name: CloseButtonPageControl
|
|
Copyright: Copyright (C) 2003-2017 SIL International.
|
|
Documentation:
|
|
Description:
|
|
Create Date: 24 Jul 2015
|
|
|
|
Modified Date: 24 Jul 2015
|
|
Authors: mcdurdin
|
|
Related Files:
|
|
Dependencies:
|
|
|
|
Bugs:
|
|
Todo:
|
|
Notes:
|
|
History: 24 Jul 2015 - mcdurdin - I4796 - Refresh Keyman Developer look and feel for release
|
|
*)
|
|
unit CloseButtonPageControl;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes,
|
|
System.Types,
|
|
Winapi.Messages,
|
|
Winapi.UxTheme,
|
|
Winapi.Windows,
|
|
Vcl.ComCtrls,
|
|
Vcl.Controls,
|
|
Vcl.Graphics,
|
|
Vcl.Styles,
|
|
Vcl.Themes;
|
|
|
|
type
|
|
TCloseButtonPageControlCloseTabEvent = procedure(Sender: TObject; Index: Integer) of object;
|
|
|
|
TCloseButtonPageControl = class(TPageControl)
|
|
private
|
|
FOnCloseTab: TCloseButtonPageControlCloseTabEvent;
|
|
FCloseButtonsRect: array of TRect;
|
|
FCloseButtonMouseDownIndex: Integer;
|
|
FCloseButtonShowPushed: Boolean;
|
|
FHintControl: THintWindow;
|
|
class constructor Create;
|
|
|
|
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
|
|
procedure InitCloseButtons;
|
|
procedure ClearTabHint;
|
|
procedure DoShowTabHint(Index: Integer);
|
|
protected
|
|
procedure DoCloseTab(Index: Integer); virtual;
|
|
procedure DrawTab(TabIndex: Integer; const Rect: TRect; Active: Boolean); override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
|
|
Y: Integer); override;
|
|
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
|
|
Y: Integer); override;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation);
|
|
override;
|
|
published
|
|
property OnCloseTab: TCloseButtonPageControlCloseTabEvent read FOnCloseTab write FOnCloseTab;
|
|
end;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.Math;
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('Keyman', [TCloseButtonPageControl]);
|
|
end;
|
|
|
|
type
|
|
TTabControlStyleHookBtnClose = class(TTabControlStyleHook)
|
|
private
|
|
FHotIndex : Integer;
|
|
FWidthModified : Boolean;
|
|
procedure WMMouseMove(var Message: TMessage); message WM_MOUSEMOVE;
|
|
procedure WMLButtonUp(var Message: TWMMouse); message WM_LBUTTONUP;
|
|
function GetButtonCloseRect(Index: Integer):TRect;
|
|
strict protected
|
|
procedure DrawTab(Canvas: TCanvas; Index: Integer); override;
|
|
procedure MouseEnter; override;
|
|
procedure MouseLeave; override;
|
|
public
|
|
constructor Create(AControl: TWinControl); override;
|
|
end;
|
|
|
|
constructor TTabControlStyleHookBtnClose.Create(AControl: TWinControl);
|
|
begin
|
|
inherited;
|
|
FHotIndex:=-1;
|
|
FWidthModified:=False;
|
|
end;
|
|
|
|
procedure TTabControlStyleHookBtnClose.DrawTab(Canvas: TCanvas; Index: Integer);
|
|
var
|
|
Details : TThemedElementDetails;
|
|
ButtonR : TRect;
|
|
FButtonState: TThemedWindow;
|
|
begin
|
|
inherited;
|
|
|
|
if (FHotIndex>=0) and (Index=FHotIndex) then
|
|
FButtonState := twSmallCloseButtonHot
|
|
else
|
|
if Index = TabIndex then
|
|
FButtonState := twSmallCloseButtonNormal
|
|
else
|
|
FButtonState := twSmallCloseButtonDisabled;
|
|
|
|
Details := StyleServices.GetElementDetails(FButtonState);
|
|
|
|
ButtonR:= GetButtonCloseRect(Index);
|
|
if ButtonR.Bottom - ButtonR.Top > 0 then
|
|
StyleServices.DrawElement(Canvas.Handle, Details, ButtonR);
|
|
end;
|
|
|
|
procedure TTabControlStyleHookBtnClose.WMLButtonUp(var Message: TWMMouse);
|
|
Var
|
|
LPoint : TPoint;
|
|
LIndex : Integer;
|
|
begin
|
|
LPoint:=Message.Pos;
|
|
for LIndex := 0 to TabCount-1 do
|
|
if PtInRect(GetButtonCloseRect(LIndex), LPoint) then
|
|
begin
|
|
if Control is TPageControl then
|
|
begin
|
|
TCloseButtonPageControl(Control).DoCloseTab(LIndex);
|
|
end;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TTabControlStyleHookBtnClose.WMMouseMove(var Message: TMessage);
|
|
Var
|
|
LPoint : TPoint;
|
|
LIndex : Integer;
|
|
LHotIndex : Integer;
|
|
begin
|
|
inherited;
|
|
LHotIndex:=-1;
|
|
LPoint:=TWMMouseMove(Message).Pos;
|
|
for LIndex := 0 to TabCount-1 do
|
|
if PtInRect(GetButtonCloseRect(LIndex), LPoint) then
|
|
begin
|
|
LHotIndex:=LIndex;
|
|
break;
|
|
end;
|
|
|
|
if (FHotIndex<>LHotIndex) then
|
|
begin
|
|
FHotIndex:=LHotIndex;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
function TTabControlStyleHookBtnClose.GetButtonCloseRect(Index: Integer): TRect;
|
|
var
|
|
FButtonState: TThemedWindow;
|
|
Details : TThemedElementDetails;
|
|
R, ButtonR : TRect;
|
|
begin
|
|
R := TabRect[Index];
|
|
if R.Left < 0 then Exit;
|
|
|
|
if TabPosition in [tpTop, tpBottom] then
|
|
begin
|
|
if Index = TabIndex then
|
|
InflateRect(R, 0, 2);
|
|
end
|
|
else
|
|
if Index = TabIndex then
|
|
Dec(R.Left, 2)
|
|
else
|
|
Dec(R.Right, 2);
|
|
|
|
Result := R;
|
|
FButtonState := twSmallCloseButtonNormal;
|
|
|
|
Details := StyleServices.GetElementDetails(FButtonState);
|
|
if not StyleServices.GetElementContentRect(0, Details, Result, ButtonR) then
|
|
ButtonR := Rect(0, 0, 0, 0);
|
|
|
|
Result.Left :=Result.Right - (ButtonR.Width) - 5;
|
|
Result.Width:=ButtonR.Width;
|
|
Result.Height:=ButtonR.Height;
|
|
Result.Top := (R.Height - Result.Height) div 2;
|
|
end;
|
|
|
|
procedure TTabControlStyleHookBtnClose.MouseEnter;
|
|
begin
|
|
inherited;
|
|
FHotIndex := -1;
|
|
end;
|
|
|
|
procedure TTabControlStyleHookBtnClose.MouseLeave;
|
|
begin
|
|
inherited;
|
|
if FHotIndex >= 0 then
|
|
begin
|
|
FHotIndex := -1;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
{ TCloseButtonPageControl }
|
|
|
|
procedure TCloseButtonPageControl.CMMouseLeave(var Message: TMessage);
|
|
begin
|
|
FCloseButtonShowPushed := False;
|
|
ClearTabHint;
|
|
Invalidate;
|
|
end;
|
|
|
|
class constructor TCloseButtonPageControl.Create;
|
|
begin
|
|
TCustomStyleEngine.RegisterStyleHook(TCloseButtonPageControl, TTabControlStyleHookBtnClose);
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.DoCloseTab(Index: Integer);
|
|
begin
|
|
if Assigned(FOnCloseTab) then
|
|
FOnCloseTab(Self, Index)
|
|
else
|
|
begin
|
|
Pages[Index].Parent:=nil;
|
|
Pages[Index].Free;
|
|
end;
|
|
end;
|
|
|
|
{ Close buttons on page control - theme services not in use }
|
|
|
|
procedure TCloseButtonPageControl.InitCloseButtons;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
// TabWidth := 0;
|
|
// OwnerDraw := False;
|
|
// OwnerDraw := True;
|
|
|
|
//should be done on every change of the page count
|
|
SetLength(FCloseButtonsRect, PageCount);
|
|
FCloseButtonMouseDownIndex := -1;
|
|
|
|
for I := 0 to Length(FCloseButtonsRect) - 1 do
|
|
FCloseButtonsRect[I] := Rect(0, 0, 0, 0);
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.MouseDown(Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited;
|
|
if Button = mbLeft then
|
|
begin
|
|
for I := 0 to Length(FCloseButtonsRect) - 1 do
|
|
begin
|
|
if PtInRect(FCloseButtonsRect[I], Point(X, Y)) then
|
|
begin
|
|
FCloseButtonMouseDownIndex := I;
|
|
FCloseButtonShowPushed := True;
|
|
InvalidateRect(Handle, FCloseButtonsRect[i], False);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.DoShowTabHint(Index: Integer);
|
|
var
|
|
R: TRect;
|
|
begin
|
|
if Pages[Index].Hint <> '' then
|
|
begin
|
|
if not Assigned(FHintControl) then
|
|
begin
|
|
FHintControl := THintWindow.Create(Self);
|
|
FHintControl.Tag := -1;
|
|
end;
|
|
|
|
if FHintControl.Tag <> Index then
|
|
begin
|
|
ClearTabHint;
|
|
FHintControl.Tag := Index;
|
|
R := FHintControl.CalcHintRect(840, Pages[Index].Hint, nil);
|
|
with TabRect(Index) do
|
|
with ClientToScreen(Point(Left, Bottom)) do
|
|
OffsetRect(R, X + 2, Y + 2);
|
|
FHintControl.ActivateHint(R, Pages[Index].Hint);
|
|
end;
|
|
end
|
|
else if Assigned(FHintControl) and (FHintControl.Tag <> Index) then
|
|
ClearTabHint;
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.ClearTabHint;
|
|
begin
|
|
if Assigned(FHintControl) then
|
|
begin
|
|
FHintControl.ReleaseHandle;
|
|
FHintControl.Tag := -1;
|
|
end;
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
Inside: Boolean;
|
|
i: Integer;
|
|
begin
|
|
inherited;
|
|
|
|
// Tab hints
|
|
i := IndexOfTabAt(X, Y);
|
|
if i >= 0 then
|
|
DoShowTabHint(i)
|
|
else
|
|
ClearTabHint;
|
|
|
|
// Close buttons
|
|
if (ssLeft in Shift) and (FCloseButtonMouseDownIndex >= 0) then
|
|
begin
|
|
Inside := PtInRect(FCloseButtonsRect[FCloseButtonMouseDownIndex], Point(X, Y));
|
|
|
|
if FCloseButtonShowPushed <> Inside then
|
|
begin
|
|
FCloseButtonShowPushed := Inside;
|
|
InvalidateRect(Handle, FCloseButtonsRect[FCloseButtonMouseDownIndex], False);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.MouseUp(Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
inherited;
|
|
if (Button = mbLeft) and (FCloseButtonMouseDownIndex >= 0) then
|
|
begin
|
|
if PtInRect(FCloseButtonsRect[FCloseButtonMouseDownIndex], Point(X, Y)) then
|
|
begin
|
|
DoCloseTab(FCloseButtonMouseDownIndex);
|
|
InvalidateRect(Handle, FCloseButtonsRect[FCloseButtonMouseDownIndex], False);
|
|
FCloseButtonMouseDownIndex := -1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
if (AComponent is TTabSheet) then
|
|
if AComponent <> Self then
|
|
SetLength(FCloseButtonsRect, 0);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TCloseButtonPageControl.DrawTab(TabIndex: Integer; const Rect: TRect; Active: Boolean);
|
|
var
|
|
CloseBtnSize: Integer;
|
|
TabCaption: TPoint;
|
|
CloseBtnRect: TRect;
|
|
CloseBtnDrawState: Cardinal;
|
|
CloseBtnDrawDetails: TThemedElementDetails;
|
|
begin
|
|
if Length(FCloseButtonsRect) = 0 then
|
|
InitCloseButtons;
|
|
|
|
if InRange(TabIndex, 0, Length(FCloseButtonsRect) - 1) then
|
|
begin
|
|
CloseBtnSize := 14;
|
|
TabCaption.Y := Rect.Top + 3;
|
|
|
|
if Active then
|
|
begin
|
|
CloseBtnRect.Top := Rect.Top + 4;
|
|
CloseBtnRect.Right := Rect.Right - 5;
|
|
TabCaption.X := Rect.Left + 6;
|
|
Canvas.Brush.Color := $DAC379; // Keyman Light Blue
|
|
end
|
|
else
|
|
begin
|
|
CloseBtnRect.Top := Rect.Top + 3;
|
|
CloseBtnRect.Right := Rect.Right - 5;
|
|
TabCaption.X := Rect.Left + 3;
|
|
end;
|
|
|
|
CloseBtnRect.Bottom := CloseBtnRect.Top + CloseBtnSize;
|
|
CloseBtnRect.Left := CloseBtnRect.Right - CloseBtnSize;
|
|
FCloseButtonsRect[TabIndex] := CloseBtnRect;
|
|
|
|
Canvas.FillRect(Rect);
|
|
Canvas.TextOut(TabCaption.X, TabCaption.Y, Pages[TabIndex].Caption);
|
|
|
|
if not UseThemes then
|
|
begin
|
|
if (FCloseButtonMouseDownIndex = TabIndex) and FCloseButtonShowPushed then
|
|
CloseBtnDrawState := DFCS_CAPTIONCLOSE + DFCS_PUSHED
|
|
else
|
|
CloseBtnDrawState := DFCS_CAPTIONCLOSE;
|
|
|
|
Winapi.Windows.DrawFrameControl(Canvas.Handle,
|
|
FCloseButtonsRect[TabIndex], DFC_CAPTION, CloseBtnDrawState);
|
|
end
|
|
else
|
|
begin
|
|
Dec(FCloseButtonsRect[TabIndex].Left);
|
|
|
|
if (FCloseButtonMouseDownIndex = TabIndex) and FCloseButtonShowPushed then
|
|
CloseBtnDrawDetails := StyleServices.GetElementDetails(twCloseButtonPushed)
|
|
else
|
|
CloseBtnDrawDetails := StyleServices.GetElementDetails(twCloseButtonNormal);
|
|
|
|
StyleServices.DrawElement(Canvas.Handle, CloseBtnDrawDetails,
|
|
FCloseButtonsRect[TabIndex]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|