mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
Extract TFlatDropDown reusable dropdown component
Moves CTCSS tone picker and FM step picker from inline TPanel+TFlatButton arrays into a standalone FlatDropDown.pas module. TFlatDropDown owns the popup panel, handles positioning and item selection, and fires OnSelect. ApplyStyle syncs button colors on theme change. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
unit FlatDropDown;
|
||||
|
||||
{ Выпадающий список на основе TFlatButton.
|
||||
Управляет попап-панелью с кнопками-элементами.
|
||||
|
||||
Использование:
|
||||
FDrop := TFlatDropDown.Create(SelectorBtn, PopupParent, Cols, BtnH);
|
||||
FDrop.SetItems(ItemNames);
|
||||
FDrop.SetItemIndex(DefaultIdx);
|
||||
FDrop.OnSelect := @MySelectHandler;
|
||||
// В обработчике кнопки-якоря:
|
||||
FDrop.Toggle;
|
||||
// При смене темы:
|
||||
FDrop.ApplyStyle(@StyleButton, T.Panel); }
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Controls, Graphics, ExtCtrls, FlatButton;
|
||||
|
||||
type
|
||||
TButtonStyleProc = procedure(B: TFlatButton; Active: Boolean) of object;
|
||||
TDropDownSelectEvent = procedure(Sender: TObject; Idx: Integer) of object;
|
||||
|
||||
TFlatDropDown = class
|
||||
private
|
||||
FAnchor: TFlatButton;
|
||||
FPopupParent: TWinControl;
|
||||
FPopup: TPanel;
|
||||
FItemBtns: array of TFlatButton;
|
||||
FItemIndex: Integer;
|
||||
FColumns: Integer;
|
||||
FItemBtnH: Integer;
|
||||
FIsOpen: Boolean;
|
||||
FOnSelect: TDropDownSelectEvent;
|
||||
procedure ItemClick(Sender: TObject);
|
||||
procedure PositionPopup;
|
||||
public
|
||||
constructor Create(AAnchor: TFlatButton; APopupParent: TWinControl;
|
||||
ACols, ABtnH: Integer);
|
||||
destructor Destroy; override;
|
||||
procedure SetItems(const ANames: array of string);
|
||||
procedure SetItemIndex(Idx: Integer);
|
||||
property ItemIndex: Integer read FItemIndex;
|
||||
property IsOpen: Boolean read FIsOpen;
|
||||
procedure Toggle;
|
||||
procedure ClosePopup;
|
||||
procedure ApplyStyle(AStyleProc: TButtonStyleProc; APanelColor: TColor);
|
||||
property OnSelect: TDropDownSelectEvent read FOnSelect write FOnSelect;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
constructor TFlatDropDown.Create(AAnchor: TFlatButton; APopupParent: TWinControl;
|
||||
ACols, ABtnH: Integer);
|
||||
begin
|
||||
inherited Create;
|
||||
FAnchor := AAnchor;
|
||||
FPopupParent := APopupParent;
|
||||
FColumns := ACols;
|
||||
FItemBtnH := ABtnH;
|
||||
FItemIndex := 0;
|
||||
FIsOpen := False;
|
||||
FPopup := TPanel.Create(nil);
|
||||
FPopup.Parent := APopupParent;
|
||||
FPopup.BevelOuter := bvNone;
|
||||
FPopup.Color := TColor($00181818);
|
||||
FPopup.Visible := False;
|
||||
end;
|
||||
|
||||
destructor TFlatDropDown.Destroy;
|
||||
begin
|
||||
FPopup.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.SetItems(const ANames: array of string);
|
||||
var
|
||||
I, Count, BtnW, PopW: Integer;
|
||||
B: TFlatButton;
|
||||
begin
|
||||
while FPopup.ControlCount > 0 do
|
||||
FPopup.Controls[0].Free;
|
||||
Count := Length(ANames);
|
||||
SetLength(FItemBtns, Count);
|
||||
PopW := FPopupParent.ClientWidth;
|
||||
BtnW := (PopW - 4) div FColumns;
|
||||
FPopup.SetBounds(0, 0, PopW,
|
||||
((Count + FColumns - 1) div FColumns) * FItemBtnH);
|
||||
for I := 0 to Count - 1 do
|
||||
begin
|
||||
B := MakeFlatBtn(FPopup, ANames[I],
|
||||
2 + (I mod FColumns) * BtnW,
|
||||
(I div FColumns) * FItemBtnH,
|
||||
BtnW - 2, FItemBtnH, @ItemClick);
|
||||
B.Tag := I;
|
||||
B.Active := (I = FItemIndex);
|
||||
FItemBtns[I] := B;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.SetItemIndex(Idx: Integer);
|
||||
begin
|
||||
if (Idx < 0) or (Idx >= Length(FItemBtns)) then Exit;
|
||||
if (FItemIndex <> Idx) and (FItemIndex >= 0) and
|
||||
(FItemIndex < Length(FItemBtns)) and (FItemBtns[FItemIndex] <> nil) then
|
||||
FItemBtns[FItemIndex].Active := False;
|
||||
FItemIndex := Idx;
|
||||
if FItemBtns[Idx] <> nil then
|
||||
FItemBtns[Idx].Active := True;
|
||||
if FAnchor <> nil then
|
||||
begin
|
||||
FAnchor.Caption := FItemBtns[Idx].Caption;
|
||||
FAnchor.Invalidate;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.ItemClick(Sender: TObject);
|
||||
var
|
||||
Idx: Integer;
|
||||
begin
|
||||
Idx := (Sender as TFlatButton).Tag;
|
||||
ClosePopup;
|
||||
SetItemIndex(Idx);
|
||||
if Assigned(FOnSelect) then FOnSelect(Self, Idx);
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.PositionPopup;
|
||||
var
|
||||
P: TPoint;
|
||||
Y0: Integer;
|
||||
begin
|
||||
P := FAnchor.ClientToScreen(Point(0, FAnchor.Height));
|
||||
P := FPopupParent.ScreenToClient(P);
|
||||
Y0 := P.Y + 2;
|
||||
if Y0 + FPopup.Height > FPopupParent.ClientHeight then
|
||||
Y0 := P.Y - FAnchor.Height - FPopup.Height - 2;
|
||||
if Y0 < 0 then Y0 := 0;
|
||||
FPopup.Top := Y0;
|
||||
FPopup.Left := 0;
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.Toggle;
|
||||
begin
|
||||
if FIsOpen then
|
||||
begin
|
||||
ClosePopup;
|
||||
Exit;
|
||||
end;
|
||||
PositionPopup;
|
||||
FPopup.BringToFront;
|
||||
FPopup.Visible := True;
|
||||
FIsOpen := True;
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.ClosePopup;
|
||||
begin
|
||||
if not FIsOpen then Exit;
|
||||
FPopup.Visible := False;
|
||||
FIsOpen := False;
|
||||
end;
|
||||
|
||||
procedure TFlatDropDown.ApplyStyle(AStyleProc: TButtonStyleProc; APanelColor: TColor);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
FPopup.Color := APanelColor;
|
||||
for I := 0 to High(FItemBtns) do
|
||||
if FItemBtns[I] <> nil then
|
||||
AStyleProc(FItemBtns[I], FItemBtns[I].Active);
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user