doublecmd/components/KASToolBar/kascombobox.pas

466 lines
12 KiB
ObjectPascal

{
Double Commander Components
-------------------------------------------------------------------------
Extended ComboBox classes
Copyright (C) 2012 Przemyslaw Nagay (cobines@gmail.com)
Copyright (C) 2015-2023 Alexander Koblov (alexx2000@mail.ru)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
}
unit KASComboBox;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
ColorBox, Buttons, LMessages, Types, KASButton;
const
DEF_COLOR_STYLE = [cbStandardColors, cbExtendedColors,
cbSystemColors, cbPrettyNames];
type
{ TComboBoxWithDelItems }
{en
Combo box that allows removing items with Shift+Delete.
}
TComboBoxWithDelItems = class(TComboBox)
protected
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
end;
{ TComboBoxAutoWidth }
TComboBoxAutoWidth = class(TComboBox)
protected
procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer;
WithThemeSpace: Boolean); override;
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
end;
{ TKASColorBox }
TKASColorBox = class(TColorBox)
protected
procedure SetCustomColor(AColor: TColor);
function PickCustomColor: Boolean; override;
procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer;
WithThemeSpace: Boolean); override;
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
public
constructor Create(AOwner: TComponent); override;
published
property DefaultColorColor default clNone;
property Style default DEF_COLOR_STYLE;
end;
{ TKASColorBoxButton }
TKASColorBoxButton = class(TCustomControl)
private
function GetSelected: TColor;
function GetStyle: TColorBoxStyle;
function GetOnChange: TNotifyEvent;
function GetColorDialog: TColorDialog;
procedure SetSelected(AValue: TColor);
procedure SetStyle(AValue: TColorBoxStyle);
procedure SetOnChange(AValue: TNotifyEvent);
procedure SetColorDialog(AValue: TColorDialog);
protected
FButton: TKASButton;
FColorBox: TKASColorBox;
procedure DoAutoSize; override;
procedure EnabledChanged; override;
procedure ButtonClick(Sender: TObject);
class function GetControlClassDefaultSize: TSize; override;
procedure CMParentColorChanged(var Message: TLMessage); message CM_PARENTCOLORCHANGED;
public
constructor Create(AOwner: TComponent); override;
procedure SetFocus; override;
function Focused: Boolean; override;
property Selected: TColor read GetSelected write SetSelected default clBlack;
published
property Align;
property Anchors;
property TabOrder;
property Constraints;
property BorderSpacing;
property AutoSize default True;
property OnChange: TNotifyEvent read GetOnChange write SetOnChange;
property ColorDialog: TColorDialog read GetColorDialog write SetColorDialog;
property Style: TColorBoxStyle read GetStyle write SetStyle default DEF_COLOR_STYLE;
end;
procedure Register;
implementation
uses
LCLType, LCLIntf;
procedure Register;
begin
RegisterComponents('KASComponents',[TComboBoxWithDelItems, TComboBoxAutoWidth,
TKASColorBox, TKASColorBoxButton]);
end;
procedure CalculateSize(ComboBox: TCustomComboBox;
var PreferredWidth: Integer; PreferredHeight: Integer);
var
DC: HDC;
R: TRect;
I, M: Integer;
Flags: Cardinal;
OldFont: HGDIOBJ;
MaxWidth: Integer;
LabelText: String;
Idx: Integer = -1;
begin
with ComboBox do
begin
MaxWidth:= Constraints.MinMaxWidth(10000);
if Items.Count = 0 then
LabelText:= Text
else begin
M := Canvas.TextWidth(Text);
for I := 0 to Items.Count - 1 do
begin
Flags := Canvas.TextWidth(Items[I]);
if Flags > M then
begin
M := Flags;
Idx := I;
end;
end;
if Idx < 0 then
LabelText := Text
else begin
LabelText := Items[Idx];
end;
end;
if LabelText = '' then begin
PreferredWidth := 1;
Exit;
end;
DC := GetDC(Parent.Handle);
try
LabelText:= LabelText + 'W';
R := Rect(0, 0, MaxWidth, 10000);
OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
Flags := DT_CALCRECT or DT_EXPANDTABS;
DrawText(DC, PChar(LabelText), Length(LabelText), R, Flags);
SelectObject(DC, OldFont);
PreferredWidth := (R.Right - R.Left) + PreferredHeight;
finally
ReleaseDC(Parent.Handle, DC);
end;
end;
end;
function CalculateHeight(ComboBox: TCustomComboBox): Integer;
var
DC: HDC;
R: TRect;
Flags: Cardinal;
OldFont: HGDIOBJ;
LabelText: String;
MaxHeight: Integer;
begin
with ComboBox do
begin
MaxHeight:= Constraints.MinMaxHeight(10000);
DC := GetDC(Parent.Handle);
try
LabelText:= Items.Text;
R := Rect(0, 0, 10000, MaxHeight);
OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
Flags := DT_CALCRECT or DT_EXPANDTABS or DT_SINGLELINE;
DrawText(DC, PChar(LabelText), Length(LabelText), R, Flags);
SelectObject(DC, OldFont);
Result := (R.Bottom - R.Top);
finally
ReleaseDC(Parent.Handle, DC);
end;
end;
end;
{ TComboBoxWithDelItems }
procedure TComboBoxWithDelItems.KeyDown(var Key: Word; Shift: TShiftState);
var
Index: Integer;
begin
if DroppedDown and (Key = VK_DELETE) and (Shift = [ssShift]) then
begin
Index := ItemIndex;
if (Index >= 0) and (Index < Items.Count) then
begin
Items.Delete(Index);
ItemIndex := Index;
Key := 0;
end;
end;
inherited KeyDown(Key, Shift);
end;
{ TComboBoxAutoWidth }
procedure TComboBoxAutoWidth.CalculatePreferredSize(var PreferredWidth,
PreferredHeight: Integer; WithThemeSpace: Boolean);
begin
inherited CalculatePreferredSize(PreferredWidth, PreferredHeight, WithThemeSpace);
if csDesigning in ComponentState then Exit;
if (Parent = nil) or (not Parent.HandleAllocated) then Exit;
CalculateSize(Self, PreferredWidth, PreferredHeight);
end;
procedure TComboBoxAutoWidth.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double);
begin
// Don't auto adjust horizontal layout
inherited DoAutoAdjustLayout(AMode, 1.0, AYProportion);
end;
{ TKASColorBox }
procedure TKASColorBox.SetCustomColor(AColor: TColor);
var
Index: Integer;
begin
for Index:= Ord(cbCustomColor in Style) to Items.Count - 1 do
begin
if Colors[Index] = AColor then
begin
Selected:= AColor;
Exit;
end;
end;
if cbCustomColor in Style then
begin
Items.Objects[0]:= TObject(PtrInt(AColor));
end;
Items.AddObject('$' + HexStr(AColor, 8), TObject(PtrInt(AColor)));
Selected:= AColor;
end;
function TKASColorBox.PickCustomColor: Boolean;
begin
Result:= inherited PickCustomColor;
SetCustomColor(Colors[0]);
end;
procedure TKASColorBox.CalculatePreferredSize(var PreferredWidth,
PreferredHeight: Integer; WithThemeSpace: Boolean);
begin
inherited CalculatePreferredSize(PreferredWidth, PreferredHeight, WithThemeSpace);
if csDesigning in ComponentState then Exit;
if (Parent = nil) or (not Parent.HandleAllocated) then Exit;
if (csSubComponent in ComponentStyle) then
begin
ItemHeight:= CalculateHeight(Self);
if (Parent.Anchors * [akLeft, akRight] = [akLeft, akRight]) then
Exit;
end;
CalculateSize(Self, PreferredWidth, PreferredHeight);
PreferredWidth+= ColorRectWidth + ColorRectOffset;
end;
procedure TKASColorBox.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double);
begin
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
begin
if ColorRectWidthStored then
ColorRectWidth:= Round(ColorRectWidth * AXProportion);
end;
// Don't auto adjust horizontal layout
inherited DoAutoAdjustLayout(AMode, 1.0, AYProportion);
end;
constructor TKASColorBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Style:= DEF_COLOR_STYLE;
DefaultColorColor:= clNone;
end;
{ TKASColorBoxButton }
function TKASColorBoxButton.GetSelected: TColor;
begin
Result:= FColorBox.Selected;
end;
function TKASColorBoxButton.GetStyle: TColorBoxStyle;
begin
Result:= FColorBox.Style;
end;
function TKASColorBoxButton.GetOnChange: TNotifyEvent;
begin
Result:= FColorBox.OnChange;
end;
function TKASColorBoxButton.GetColorDialog: TColorDialog;
begin
Result:= FColorBox.ColorDialog;
end;
procedure TKASColorBoxButton.SetSelected(AValue: TColor);
begin
FColorBox.SetCustomColor(AValue);
end;
procedure TKASColorBoxButton.SetStyle(AValue: TColorBoxStyle);
begin
FColorBox.Style:= AValue;
end;
procedure TKASColorBoxButton.SetOnChange(AValue: TNotifyEvent);
begin
FColorBox.OnChange:= AValue;
end;
procedure TKASColorBoxButton.SetColorDialog(AValue: TColorDialog);
begin
FColorBox.ColorDialog:= AValue;
end;
procedure TKASColorBoxButton.DoAutoSize;
begin
inherited DoAutoSize;
FButton.Constraints.MinWidth:= FButton.Height;
end;
procedure TKASColorBoxButton.EnabledChanged;
begin
if Enabled then
FColorBox.Font.Color:= clDefault
else begin
FColorBox.Font.Color:= clGrayText;
end;
inherited EnabledChanged;
end;
procedure TKASColorBoxButton.ButtonClick(Sender: TObject);
Var
FreeDialog: Boolean;
begin
if csDesigning in ComponentState then Exit;
with FColorBox do
begin
FreeDialog:= (ColorDialog = nil);
if FreeDialog then
begin
ColorDialog:= TColorDialog.Create(GetTopParent);
end;
try
with ColorDialog do
begin
Color:= FColorBox.Selected;
if Execute Then
begin
FColorBox.SetCustomColor(Color);
Invalidate;
end;
end;
finally
if FreeDialog Then
begin
ColorDialog.Free;
ColorDialog:= nil;
end;
end;
end;
end;
class function TKASColorBoxButton.GetControlClassDefaultSize: TSize;
begin
Result:= TKASColorBox.GetControlClassDefaultSize;
Result.cx += Result.cy;
end;
procedure TKASColorBoxButton.CMParentColorChanged(var Message: TLMessage);
begin
if inherited ParentColor then
begin
inherited SetColor(Parent.Color);
inherited ParentColor:= True;
end;
end;
constructor TKASColorBoxButton.Create(AOwner: TComponent);
begin
FButton:= TKASButton.Create(Self);
FColorBox:= TKASColorBox.Create(Self);
inherited Create(AOwner);
ControlStyle:= ControlStyle + [csNoFocus];
BorderStyle:= bsNone;
TabStop:= True;
inherited TabStop:= False;
with FColorBox do
begin
SetSubComponent(True);
Align:= alClient;
ParentColor:= False;
ParentFont:= True;
Parent:= Self;
end;
with FButton do
begin
Align:= alRight;
Caption:= '..';
BorderSpacing.Left:= 2;
OnClick:= @ButtonClick;
Parent:= Self;
end;
AutoSize:= True;
Color:= clWindow;
inherited ParentColor:= True;
end;
procedure TKASColorBoxButton.SetFocus;
begin
FColorBox.SetFocus;
end;
function TKASColorBoxButton.Focused: Boolean;
begin
Result:= FColorBox.Focused;
end;
end.