kascombobox.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. {
  2. Double Commander Components
  3. -------------------------------------------------------------------------
  4. Extended ComboBox classes
  5. Copyright (C) 2012 Przemyslaw Nagay ([email protected])
  6. Copyright (C) 2015-2023 Alexander Koblov ([email protected])
  7. This program is free software; you can redistribute it and/or
  8. modify it under the terms of the GNU General Public License as
  9. published by the Free Software Foundation; either version 2 of the
  10. License, or (at your option) any later version.
  11. This program is distributed in the hope that it will be useful, but
  12. WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. }
  18. unit KASComboBox;
  19. {$mode objfpc}{$H+}
  20. interface
  21. uses
  22. Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
  23. ColorBox, Buttons, LMessages, Types, KASButton;
  24. const
  25. DEF_COLOR_STYLE = [cbStandardColors, cbExtendedColors,
  26. cbSystemColors, cbPrettyNames];
  27. type
  28. { TComboBoxWithDelItems }
  29. {en
  30. Combo box that allows removing items with Shift+Delete.
  31. }
  32. TComboBoxWithDelItems = class(TComboBox)
  33. protected
  34. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  35. end;
  36. { TComboBoxAutoWidth }
  37. TComboBoxAutoWidth = class(TComboBox)
  38. protected
  39. procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer;
  40. WithThemeSpace: Boolean); override;
  41. procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
  42. const AXProportion, AYProportion: Double); override;
  43. end;
  44. { TKASColorBox }
  45. TKASColorBox = class(TColorBox)
  46. protected
  47. procedure SetCustomColor(AColor: TColor);
  48. function PickCustomColor: Boolean; override;
  49. procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer;
  50. WithThemeSpace: Boolean); override;
  51. procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
  52. const AXProportion, AYProportion: Double); override;
  53. public
  54. constructor Create(AOwner: TComponent); override;
  55. published
  56. property DefaultColorColor default clNone;
  57. property Style default DEF_COLOR_STYLE;
  58. end;
  59. { TKASColorBoxButton }
  60. TKASColorBoxButton = class(TCustomControl)
  61. private
  62. function GetSelected: TColor;
  63. function GetStyle: TColorBoxStyle;
  64. function GetOnChange: TNotifyEvent;
  65. function GetColorDialog: TColorDialog;
  66. procedure SetSelected(AValue: TColor);
  67. procedure SetStyle(AValue: TColorBoxStyle);
  68. procedure SetOnChange(AValue: TNotifyEvent);
  69. procedure SetColorDialog(AValue: TColorDialog);
  70. protected
  71. FButton: TKASButton;
  72. FColorBox: TKASColorBox;
  73. procedure DoAutoSize; override;
  74. procedure EnabledChanged; override;
  75. procedure ButtonClick(Sender: TObject);
  76. class function GetControlClassDefaultSize: TSize; override;
  77. procedure CMParentColorChanged(var Message: TLMessage); message CM_PARENTCOLORCHANGED;
  78. public
  79. constructor Create(AOwner: TComponent); override;
  80. procedure SetFocus; override;
  81. function Focused: Boolean; override;
  82. property Selected: TColor read GetSelected write SetSelected default clBlack;
  83. published
  84. property Align;
  85. property Anchors;
  86. property TabOrder;
  87. property Constraints;
  88. property BorderSpacing;
  89. property AutoSize default True;
  90. property OnChange: TNotifyEvent read GetOnChange write SetOnChange;
  91. property ColorDialog: TColorDialog read GetColorDialog write SetColorDialog;
  92. property Style: TColorBoxStyle read GetStyle write SetStyle default DEF_COLOR_STYLE;
  93. end;
  94. procedure Register;
  95. implementation
  96. uses
  97. LCLType, LCLIntf;
  98. procedure Register;
  99. begin
  100. RegisterComponents('KASComponents',[TComboBoxWithDelItems, TComboBoxAutoWidth,
  101. TKASColorBox, TKASColorBoxButton]);
  102. end;
  103. procedure CalculateSize(ComboBox: TCustomComboBox;
  104. var PreferredWidth: Integer; PreferredHeight: Integer);
  105. var
  106. DC: HDC;
  107. R: TRect;
  108. I, M: Integer;
  109. Flags: Cardinal;
  110. OldFont: HGDIOBJ;
  111. MaxWidth: Integer;
  112. LabelText: String;
  113. Idx: Integer = -1;
  114. begin
  115. with ComboBox do
  116. begin
  117. MaxWidth:= Constraints.MinMaxWidth(10000);
  118. if Items.Count = 0 then
  119. LabelText:= Text
  120. else begin
  121. M := Canvas.TextWidth(Text);
  122. for I := 0 to Items.Count - 1 do
  123. begin
  124. Flags := Canvas.TextWidth(Items[I]);
  125. if Flags > M then
  126. begin
  127. M := Flags;
  128. Idx := I;
  129. end;
  130. end;
  131. if Idx < 0 then
  132. LabelText := Text
  133. else begin
  134. LabelText := Items[Idx];
  135. end;
  136. end;
  137. if LabelText = '' then begin
  138. PreferredWidth := 1;
  139. Exit;
  140. end;
  141. DC := GetDC(Parent.Handle);
  142. try
  143. LabelText:= LabelText + 'W';
  144. R := Rect(0, 0, MaxWidth, 10000);
  145. OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
  146. Flags := DT_CALCRECT or DT_EXPANDTABS;
  147. DrawText(DC, PChar(LabelText), Length(LabelText), R, Flags);
  148. SelectObject(DC, OldFont);
  149. PreferredWidth := (R.Right - R.Left) + PreferredHeight;
  150. finally
  151. ReleaseDC(Parent.Handle, DC);
  152. end;
  153. end;
  154. end;
  155. function CalculateHeight(ComboBox: TCustomComboBox): Integer;
  156. var
  157. DC: HDC;
  158. R: TRect;
  159. Flags: Cardinal;
  160. OldFont: HGDIOBJ;
  161. LabelText: String;
  162. MaxHeight: Integer;
  163. begin
  164. with ComboBox do
  165. begin
  166. MaxHeight:= Constraints.MinMaxHeight(10000);
  167. DC := GetDC(Parent.Handle);
  168. try
  169. LabelText:= Items.Text;
  170. R := Rect(0, 0, 10000, MaxHeight);
  171. OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
  172. Flags := DT_CALCRECT or DT_EXPANDTABS or DT_SINGLELINE;
  173. DrawText(DC, PChar(LabelText), Length(LabelText), R, Flags);
  174. SelectObject(DC, OldFont);
  175. Result := (R.Bottom - R.Top);
  176. finally
  177. ReleaseDC(Parent.Handle, DC);
  178. end;
  179. end;
  180. end;
  181. { TComboBoxWithDelItems }
  182. procedure TComboBoxWithDelItems.KeyDown(var Key: Word; Shift: TShiftState);
  183. var
  184. Index: Integer;
  185. begin
  186. if DroppedDown and (Key = VK_DELETE) and (Shift = [ssShift]) then
  187. begin
  188. Index := ItemIndex;
  189. if (Index >= 0) and (Index < Items.Count) then
  190. begin
  191. Items.Delete(Index);
  192. ItemIndex := Index;
  193. Key := 0;
  194. end;
  195. end;
  196. inherited KeyDown(Key, Shift);
  197. end;
  198. { TComboBoxAutoWidth }
  199. procedure TComboBoxAutoWidth.CalculatePreferredSize(var PreferredWidth,
  200. PreferredHeight: Integer; WithThemeSpace: Boolean);
  201. begin
  202. inherited CalculatePreferredSize(PreferredWidth, PreferredHeight, WithThemeSpace);
  203. if csDesigning in ComponentState then Exit;
  204. if (Parent = nil) or (not Parent.HandleAllocated) then Exit;
  205. CalculateSize(Self, PreferredWidth, PreferredHeight);
  206. end;
  207. procedure TComboBoxAutoWidth.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
  208. const AXProportion, AYProportion: Double);
  209. begin
  210. // Don't auto adjust horizontal layout
  211. inherited DoAutoAdjustLayout(AMode, 1.0, AYProportion);
  212. end;
  213. { TKASColorBox }
  214. procedure TKASColorBox.SetCustomColor(AColor: TColor);
  215. var
  216. Index: Integer;
  217. begin
  218. for Index:= Ord(cbCustomColor in Style) to Items.Count - 1 do
  219. begin
  220. if Colors[Index] = AColor then
  221. begin
  222. Selected:= AColor;
  223. Exit;
  224. end;
  225. end;
  226. if cbCustomColor in Style then
  227. begin
  228. Items.Objects[0]:= TObject(PtrInt(AColor));
  229. end;
  230. Items.AddObject('$' + HexStr(AColor, 8), TObject(PtrInt(AColor)));
  231. Selected:= AColor;
  232. end;
  233. function TKASColorBox.PickCustomColor: Boolean;
  234. begin
  235. Result:= inherited PickCustomColor;
  236. SetCustomColor(Colors[0]);
  237. end;
  238. procedure TKASColorBox.CalculatePreferredSize(var PreferredWidth,
  239. PreferredHeight: Integer; WithThemeSpace: Boolean);
  240. begin
  241. inherited CalculatePreferredSize(PreferredWidth, PreferredHeight, WithThemeSpace);
  242. if csDesigning in ComponentState then Exit;
  243. if (Parent = nil) or (not Parent.HandleAllocated) then Exit;
  244. if (csSubComponent in ComponentStyle) then
  245. begin
  246. ItemHeight:= CalculateHeight(Self);
  247. if (Parent.Anchors * [akLeft, akRight] = [akLeft, akRight]) then
  248. Exit;
  249. end;
  250. CalculateSize(Self, PreferredWidth, PreferredHeight);
  251. PreferredWidth+= ColorRectWidth + ColorRectOffset;
  252. end;
  253. procedure TKASColorBox.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
  254. const AXProportion, AYProportion: Double);
  255. begin
  256. if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
  257. begin
  258. if ColorRectWidthStored then
  259. ColorRectWidth:= Round(ColorRectWidth * AXProportion);
  260. end;
  261. // Don't auto adjust horizontal layout
  262. inherited DoAutoAdjustLayout(AMode, 1.0, AYProportion);
  263. end;
  264. constructor TKASColorBox.Create(AOwner: TComponent);
  265. begin
  266. inherited Create(AOwner);
  267. Style:= DEF_COLOR_STYLE;
  268. DefaultColorColor:= clNone;
  269. end;
  270. { TKASColorBoxButton }
  271. function TKASColorBoxButton.GetSelected: TColor;
  272. begin
  273. Result:= FColorBox.Selected;
  274. end;
  275. function TKASColorBoxButton.GetStyle: TColorBoxStyle;
  276. begin
  277. Result:= FColorBox.Style;
  278. end;
  279. function TKASColorBoxButton.GetOnChange: TNotifyEvent;
  280. begin
  281. Result:= FColorBox.OnChange;
  282. end;
  283. function TKASColorBoxButton.GetColorDialog: TColorDialog;
  284. begin
  285. Result:= FColorBox.ColorDialog;
  286. end;
  287. procedure TKASColorBoxButton.SetSelected(AValue: TColor);
  288. begin
  289. FColorBox.SetCustomColor(AValue);
  290. end;
  291. procedure TKASColorBoxButton.SetStyle(AValue: TColorBoxStyle);
  292. begin
  293. FColorBox.Style:= AValue;
  294. end;
  295. procedure TKASColorBoxButton.SetOnChange(AValue: TNotifyEvent);
  296. begin
  297. FColorBox.OnChange:= AValue;
  298. end;
  299. procedure TKASColorBoxButton.SetColorDialog(AValue: TColorDialog);
  300. begin
  301. FColorBox.ColorDialog:= AValue;
  302. end;
  303. procedure TKASColorBoxButton.DoAutoSize;
  304. begin
  305. inherited DoAutoSize;
  306. FButton.Constraints.MinWidth:= FButton.Height;
  307. end;
  308. procedure TKASColorBoxButton.EnabledChanged;
  309. begin
  310. if Enabled then
  311. FColorBox.Font.Color:= clDefault
  312. else begin
  313. FColorBox.Font.Color:= clGrayText;
  314. end;
  315. inherited EnabledChanged;
  316. end;
  317. procedure TKASColorBoxButton.ButtonClick(Sender: TObject);
  318. Var
  319. FreeDialog: Boolean;
  320. begin
  321. if csDesigning in ComponentState then Exit;
  322. with FColorBox do
  323. begin
  324. FreeDialog:= (ColorDialog = nil);
  325. if FreeDialog then
  326. begin
  327. ColorDialog:= TColorDialog.Create(GetTopParent);
  328. end;
  329. try
  330. with ColorDialog do
  331. begin
  332. Color:= FColorBox.Selected;
  333. if Execute Then
  334. begin
  335. FColorBox.SetCustomColor(Color);
  336. Invalidate;
  337. end;
  338. end;
  339. finally
  340. if FreeDialog Then
  341. begin
  342. ColorDialog.Free;
  343. ColorDialog:= nil;
  344. end;
  345. end;
  346. end;
  347. end;
  348. class function TKASColorBoxButton.GetControlClassDefaultSize: TSize;
  349. begin
  350. Result:= TKASColorBox.GetControlClassDefaultSize;
  351. Result.cx += Result.cy;
  352. end;
  353. procedure TKASColorBoxButton.CMParentColorChanged(var Message: TLMessage);
  354. begin
  355. if inherited ParentColor then
  356. begin
  357. inherited SetColor(Parent.Color);
  358. inherited ParentColor:= True;
  359. end;
  360. end;
  361. constructor TKASColorBoxButton.Create(AOwner: TComponent);
  362. begin
  363. FButton:= TKASButton.Create(Self);
  364. FColorBox:= TKASColorBox.Create(Self);
  365. inherited Create(AOwner);
  366. ControlStyle:= ControlStyle + [csNoFocus];
  367. BorderStyle:= bsNone;
  368. TabStop:= True;
  369. inherited TabStop:= False;
  370. with FColorBox do
  371. begin
  372. SetSubComponent(True);
  373. Align:= alClient;
  374. ParentColor:= False;
  375. ParentFont:= True;
  376. Parent:= Self;
  377. end;
  378. with FButton do
  379. begin
  380. Align:= alRight;
  381. Caption:= '..';
  382. BorderSpacing.Left:= 2;
  383. OnClick:= @ButtonClick;
  384. Parent:= Self;
  385. end;
  386. AutoSize:= True;
  387. Color:= clWindow;
  388. inherited ParentColor:= True;
  389. end;
  390. procedure TKASColorBoxButton.SetFocus;
  391. begin
  392. FColorBox.SetFocus;
  393. end;
  394. function TKASColorBoxButton.Focused: Boolean;
  395. begin
  396. Result:= FColorBox.Focused;
  397. end;
  398. end.