Files
kam-mobile/vendor/kam_remake/src/controls/KM_ControlsDrop.pas
T

792 lines
19 KiB
ObjectPascal
Raw Normal View History

2026-09-12 16:50:30 +05:00
unit KM_ControlsDrop;
interface
uses
Classes,
Controls,
KromOGLUtils,
KM_Controls, KM_ControlsBase, KM_ControlsList,
KM_ResTypes, KM_ResFonts,
KM_RenderUI;
type
TKMDropCommon = class(TKMControl)
private
fDropCount: Integer;
fDropUp: Boolean;
fFont: TKMFont;
fButton: TKMButton;
fShape: TKMShape;
fAutoClose: Boolean;
fOnChange: TNotifyEvent;
fOnShowList: TNotifyEvent;
procedure UpdateDropPosition; virtual; abstract;
procedure ButtonClick(Sender: TObject);
procedure ListShow(Sender: TObject); virtual;
procedure ListClick(Sender: TObject); virtual;
procedure ListChange(Sender: TObject); virtual;
procedure ListHide(Sender: TObject); virtual;
function ListVisible: Boolean; virtual; abstract;
function GetItemIndex: SmallInt; virtual; abstract;
procedure SetItemIndex(aIndex: SmallInt); virtual; abstract;
protected
procedure DoClick(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
procedure SetLeft(aValue: Integer); override;
procedure SetTop(aValue: Integer); override;
procedure SetEnabled(aValue: Boolean); override;
procedure SetVisible(aValue: Boolean); override;
function ListKeyDown(Sender: TObject; Key: Word; Shift: TShiftState): Boolean;
procedure UpdateVisibility; override;
function GetCount: Integer; virtual; abstract;
procedure SetDropCount(const aValue: Integer); virtual;
public
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; aStyle: TKMButtonStyle;
aAutoClose: Boolean = True);
procedure Clear; virtual; abstract;
procedure OpenList;
procedure CloseList;
procedure SetOpenList(aOpen: Boolean);
procedure SwitchOpen;
property DropCount: Integer read fDropCount write SetDropCount;
property DropUp: Boolean read fDropUp write fDropUp;
property ItemIndex: SmallInt read GetItemIndex write SetItemIndex;
function IsOpen: Boolean; virtual;
property Count: Integer read GetCount;
property OnShowList: TNotifyEvent read fOnShowList write fOnShowList;
property OnChange: TNotifyEvent read fOnChange write fOnChange;
procedure Paint; override;
end;
//DropBox with a ListBox
TKMDropList = class(TKMDropCommon)
private
fCaption: UnicodeString; //Current caption (Default or from list)
fDefaultCaption: UnicodeString;
fDropWidth: Integer;
fList: TKMListBox;
fListTopIndex: Integer;
procedure UpdateDropPosition; override;
procedure ListShow(Sender: TObject); override;
procedure ListClick(Sender: TObject); override;
procedure ListChange(Sender: TObject); override;
procedure ListHide(Sender: TObject); override;
function ListVisible: Boolean; override;
function GetItem(aIndex: Integer): UnicodeString;
function GetItemIndex: smallint; override;
procedure SetItemIndex(aIndex: smallint); override;
procedure SetDropWidth(aDropWidth: Integer);
function GetShowHintWhenShort: Boolean;
procedure SetShowHintWhenShort(const aValue: Boolean);
protected
procedure SetEnabled(aValue: Boolean); override;
procedure SetVisible(aValue: Boolean); override;
function GetCount: Integer; override;
procedure SetDropCount(const aValue: Integer); override;
public
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString;
aStyle: TKMButtonStyle; aAutoClose: Boolean = True; aBackAlpha: Single = 0.85);
procedure Clear; override;
procedure Add(const aItem: UnicodeString; aTag: Integer = 0);
procedure SelectByName(const aText: UnicodeString);
procedure SelectByTag(aTag: Integer);
function HasTag(aTag: Integer): Boolean;
function GetTag(aIndex: Integer): Integer;
function GetSelectedTag: Integer;
function IsSelected: Boolean;
property ShowHintWhenShort: Boolean read GetShowHintWhenShort write SetShowHintWhenShort;
property DefaultCaption: UnicodeString read fDefaultCaption write fDefaultCaption;
property Item[aIndex: Integer]: UnicodeString read GetItem;
property List: TKMListBox read fList;
function IsOpen: Boolean; override;
property DropWidth: Integer read fDropWidth write SetDropWidth;
procedure Paint; override;
end;
//DropBox with a ColumnBox
TKMDropColumns = class(TKMDropCommon)
private
fDefaultCaption: UnicodeString;
fDropWidth: Integer;
fList: TKMColumnBox;
fListTopIndex: Integer;
fColumnsToShowWhenListHidden: array of Boolean; //which columns to show, when list is hidden
procedure UpdateDropPosition; override;
procedure ListShow(Sender: TObject); override;
procedure ListClick(Sender: TObject); override;
procedure ListChange(Sender: TObject); override;
procedure ListHide(Sender: TObject); override;
function ListVisible: Boolean; override;
function GetItem(aIndex: Integer): TKMListRow;
function GetItemIndex: Smallint; override;
procedure SetItemIndex(aIndex: Smallint); override;
procedure SetDropWidth(aDropWidth: Integer);
function GetShowHintWhenShort: Boolean;
procedure SetShowHintWhenShort(const aValue: Boolean);
protected
procedure SetEnabled(aValue: Boolean); override;
procedure SetVisible(aValue: Boolean); override;
function GetCount: Integer; override;
public
FadeImageWhenDisabled: Boolean;
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString; aStyle: TKMButtonStyle; aShowHeader: Boolean = True);
procedure Add(aItem: TKMListRow);
procedure Clear; override;
property List: TKMColumnBox read fList;
property Item[aIndex: Integer]: TKMListRow read GetItem; default;
procedure SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word); overload;
procedure SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word; aColumnsToShowWhenListHidden: array of Boolean); overload;
property DefaultCaption: UnicodeString read fDefaultCaption write fDefaultCaption;
property DropWidth: Integer read fDropWidth write SetDropWidth;
property ShowHintWhenShort: Boolean read GetShowHintWhenShort write SetShowHintWhenShort;
procedure Paint; override;
end;
implementation
uses
{$IFDEF MSWindows} Windows, {$ENDIF}
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
Math,
KM_Defaults;
{ TKMDropCommon }
constructor TKMDropCommon.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont;
aStyle: TKMButtonStyle; aAutoClose: Boolean = True);
var
P: TKMPanel;
begin
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
fDropCount := 10;
fDropUp := False;
fFont := aFont;
fButton := TKMButton.Create(aParent, aLeft+aWidth-aHeight, aTop, aHeight, aHeight, 590, rxGui, aStyle);
fButton.OnClick := ButtonClick;
fButton.MakesSound := False;
P := MasterParent;
fShape := TKMShape.Create(P, 0, 0, P.Width, P.Height);
fShape.AnchorsStretch;
fShape.OnClick := ListHide;
fAutoClose := aAutoClose;
end;
procedure TKMDropCommon.UpdateVisibility;
begin
inherited;
if not Visible then
CloseList;
end;
function TKMDropCommon.IsOpen: Boolean;
begin
Result := fShape.Visible;
end;
procedure TKMDropCommon.ButtonClick(Sender: TObject);
begin
//Call the DoDlick event to show the list AND generate DropBox.OnClick event
DoClick(fButton.AbsLeft + fButton.Width div 2, fButton.AbsTop + fButton.Height div 2, [], mbLeft);
end;
procedure TKMDropCommon.ListShow(Sender: TObject);
begin
if ListVisible then
begin
ListHide(nil);
Exit;
end;
if fAutoClose and (Count > 0) then
fShape.Show;
if Assigned(fOnShowList) then fOnShowList(Self);
end;
procedure TKMDropCommon.DoClick(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
begin
//do not invoke inherited here, to fully override parent DoClick method
//It's common behavior when click on dropbox will show the list
if fAutoClose then
ListShow(Self)
else
if not ListVisible then
begin
fButton.TexId := 591;
ListShow(Self)
end else begin
fButton.TexId := 590;
ListHide(Self);
end;
inherited;
end;
//Handle KeyDown on List
function TKMDropCommon.ListKeyDown(Sender: TObject; Key: Word; Shift: TShiftState): Boolean;
begin
if fAutoClose and (Key = VK_ESCAPE) then // Close List on ESC, if autoclosable
begin
ListHide(nil);
Result := True;
end else
Result := False;
end;
procedure TKMDropCommon.ListClick(Sender: TObject);
begin
//No need to call fOnChange here since ListChange was already called
if fAutoClose then ListHide(nil);
end;
procedure TKMDropCommon.ListChange(Sender: TObject);
begin
if (ItemIndex <> -1) then
if Assigned(fOnChange) then fOnChange(Self);
end;
procedure TKMDropCommon.ListHide(Sender: TObject);
begin
fShape.Hide;
end;
procedure TKMDropCommon.SetDropCount(const aValue: Integer);
begin
fDropCount := aValue;
end;
procedure TKMDropCommon.SetEnabled(aValue: Boolean);
begin
inherited;
fButton.Enabled := Enabled;
end;
procedure TKMDropCommon.OpenList;
begin
ListShow(nil);
end;
procedure TKMDropCommon.CloseList;
begin
ListHide(nil);
end;
procedure TKMDropCommon.SetOpenList(aOpen: Boolean);
begin
if aOpen then
OpenList
else
CloseList;
end;
procedure TKMDropCommon.SwitchOpen;
begin
if IsOpen then
CloseList
else
OpenList;
end;
procedure TKMDropCommon.SetLeft(aValue: Integer);
begin
inherited;
// Stick the button to us
fButton.Left := Left + Width - Height;
end;
procedure TKMDropCommon.SetTop(aValue: Integer);
begin
inherited;
// Stick the button to us
fButton.Top := Top;
end;
procedure TKMDropCommon.SetVisible(aValue: Boolean);
begin
inherited;
fButton.Visible := aValue;
if not aValue then ListHide(Self);
end;
procedure TKMDropCommon.Paint;
begin
inherited;
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height);
// Make sure the list stays where it needs to be relative DropBox (e.g. on window resize)
UpdateDropPosition;
end;
{ TKMDropList }
constructor TKMDropList.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString;
aStyle: TKMButtonStyle; aAutoClose: Boolean = True; aBackAlpha: Single = 0.85);
begin
inherited Create(aParent, aLeft, aTop, aWidth, aHeight, aFont, aStyle, aAutoClose);
fDefaultCaption := aDefaultCaption;
fListTopIndex := 0;
fList := TKMListBox.Create(MasterParent, 0, 0, aWidth, 0, fFont, aStyle);
fList.Height := fList.ItemHeight * fDropCount;
fList.AutoHideScrollBar := True; //A drop box should only have a scrollbar if required
fList.BackAlpha := aBackAlpha;
fList.OnClick := ListClick;
fList.OnChange := ListChange;
DropWidth := aWidth;
ListHide(nil);
fList.OnKeyDown := ListKeyDown;
end;
procedure TKMDropList.ListShow(Sender: TObject);
begin
inherited;
if ListVisible or (Count < 1) then Exit;
//Make sure the selected item is visible when list is opened
if (ItemIndex <> -1) then
begin
if InRange(ItemIndex, fListTopIndex, fListTopIndex + fList.GetVisibleRows) then //Try to open list at previously saved scroll position
fList.TopIndex := fListTopIndex
else
fList.TopIndex := ItemIndex;
end;
fList.Show;
end;
procedure TKMDropList.ListClick(Sender: TObject);
begin
fCaption := fList.Item[ItemIndex];
inherited;
end;
procedure TKMDropList.ListChange(Sender: TObject);
begin
fCaption := fList.Item[ItemIndex];
inherited;
end;
procedure TKMDropList.ListHide(Sender: TObject);
begin
fListTopIndex := fList.TopIndex; //Save list scroll position
inherited;
fList.Hide;
end;
function TKMDropList.ListVisible: Boolean;
begin
Result := fList.Visible;
end;
function TKMDropList.GetItemIndex: Smallint;
begin
Result := fList.ItemIndex;
end;
procedure TKMDropList.SetItemIndex(aIndex: Smallint);
begin
fList.ItemIndex := aIndex;
if aIndex <> -1 then
fCaption := fList.Item[fList.ItemIndex]
else
fCaption := fDefaultCaption;
end;
function TKMDropList.GetShowHintWhenShort: Boolean;
begin
Result := fList.ShowHintWhenShort;
end;
procedure TKMDropList.SetShowHintWhenShort(const aValue: Boolean);
begin
fList.ShowHintWhenShort := aValue;
end;
function TKMDropList.IsOpen: Boolean;
begin
Result := fList.Visible;
end;
procedure TKMDropList.SetDropCount(const aValue: Integer);
begin
inherited;
fList.Height := fList.ItemHeight * fDropCount;
end;
procedure TKMDropList.SetDropWidth(aDropWidth: Integer);
begin
fDropWidth := aDropWidth;
fList.AbsLeft := AbsLeft + Width - aDropWidth;
fList.Width := aDropWidth;
end;
procedure TKMDropList.SetEnabled(aValue: Boolean);
begin
inherited;
fList.Enabled := Enabled;
if not Enabled and fList.Visible then
ListHide(nil);
end;
procedure TKMDropList.SetVisible(aValue: Boolean);
begin
inherited;
if not aValue then ListHide(Self);
end;
function TKMDropList.GetCount: Integer;
begin
Result := fList.Count;
end;
// When new items are added to the list we must update the drop height and position
procedure TKMDropList.UpdateDropPosition;
begin
if Count > 0 then
begin
fList.Height := Math.Min(fDropCount, fList.Count) * fList.ItemHeight + fList.SeparatorsCount*fList.SeparatorHeight;
if fDropUp then
fList.AbsTop := AbsTop - fList.Height
else
fList.AbsTop := AbsTop + Height;
fList.Left := AbsLeft + Width - DropWidth - MasterParent.AbsLeft;
end;
end;
procedure TKMDropList.Add(const aItem: UnicodeString; aTag: Integer = 0);
begin
fList.Add(aItem, aTag);
end;
procedure TKMDropList.SelectByName(const aText: UnicodeString);
var I: Integer;
begin
fList.ItemIndex := -1;
for I := 0 to fList.Count - 1 do
if fList.Item[I] = aText then
SetItemIndex(I);
end;
procedure TKMDropList.SelectByTag(aTag: Integer);
var I: Integer;
begin
fList.ItemIndex := -1;
for I := 0 to fList.Count - 1 do
if fList.ItemTags[I] = aTag then
SetItemIndex(I);
end;
function TKMDropList.HasTag(aTag: Integer): Boolean;
var
I: Integer;
begin
Result := False;
for I := 0 to fList.Count - 1 do
if fList.ItemTags[I] = aTag then
Exit(True);
end;
function TKMDropList.IsSelected: Boolean;
begin
Result := fList.ItemIndex <> -1;
end;
function TKMDropList.GetTag(aIndex: Integer): Integer;
begin
Result := fList.ItemTags[aIndex];
end;
function TKMDropList.GetSelectedTag: Integer;
begin
Result := GetTag(fList.ItemIndex);
end;
function TKMDropList.GetItem(aIndex: Integer): UnicodeString;
begin
Result := fList.Item[aIndex];
end;
procedure TKMDropList.Clear;
begin
fList.Clear;
end;
procedure TKMDropList.Paint;
var
col: TColor4;
begin
inherited;
if Enabled then
col := icWhite
else
col := icGray2;
TKMRenderUI.WriteText(AbsLeft+4, AbsTop+4, Width-8, fCaption, fFont, taLeft, col);
end;
{ TKMDropColumns }
constructor TKMDropColumns.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString; aStyle: TKMButtonStyle; aShowHeader: Boolean = True);
begin
inherited Create(aParent, aLeft, aTop, aWidth, aHeight, aFont, aStyle);
fListTopIndex := 0;
fDefaultCaption := aDefaultCaption;
fList := TKMColumnBox.Create(MasterParent, 0, 0, aWidth, 0, fFont, aStyle);
fList.BackAlpha := 0.85;
fList.OnClick := ListClick;
fList.OnChange := ListChange;
fList.ShowHeader := aShowHeader;
DropWidth := aWidth;
ListHide(nil);
fList.OnKeyDown := ListKeyDown;
end;
procedure TKMDropColumns.ListShow(Sender: TObject);
begin
inherited;
if ListVisible or (Count < 1) then Exit;
//Make sure the selected item is visible when list is opened
if (ItemIndex <> -1) then
begin
if InRange(ItemIndex, fListTopIndex, fListTopIndex + fList.GetVisibleRows) then //Try to open list at previously saved scroll position
fList.TopIndex := fListTopIndex
else
fList.TopIndex := ItemIndex;
end;
fList.Show;
end;
procedure TKMDropColumns.ListClick(Sender: TObject);
begin
inherited;
end;
procedure TKMDropColumns.ListChange(Sender: TObject);
begin
inherited;
end;
procedure TKMDropColumns.ListHide(Sender: TObject);
begin
fListTopIndex := fList.TopIndex; //Save list scroll position
inherited;
fList.Hide;
end;
function TKMDropColumns.ListVisible: Boolean;
begin
Result := fList.Visible;
end;
function TKMDropColumns.GetItemIndex: smallint;
begin
Result := fList.ItemIndex;
end;
procedure TKMDropColumns.SetItemIndex(aIndex: smallint);
begin
fList.ItemIndex := aIndex;
end;
procedure TKMDropColumns.SetDropWidth(aDropWidth: Integer);
begin
fDropWidth := aDropWidth;
fList.AbsLeft := AbsLeft + Width - aDropWidth;
fList.Width := aDropWidth;
end;
function TKMDropColumns.GetShowHintWhenShort: Boolean;
begin
Result := fList.ShowHintWhenShort
end;
procedure TKMDropColumns.SetShowHintWhenShort(const aValue: Boolean);
begin
fList.ShowHintWhenShort := aValue;
end;
procedure TKMDropColumns.SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word);
var
I: Integer;
columnsToShowWhenListHidden: array of Boolean;
begin
SetLength(columnsToShowWhenListHidden, Length(aColumns));
for I := Low(columnsToShowWhenListHidden) to High(columnsToShowWhenListHidden) do
columnsToShowWhenListHidden[I] := True; // by default show all columns
SetColumns(aFont, aColumns, aColumnOffsets, columnsToShowWhenListHidden);
end;
procedure TKMDropColumns.SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word; aColumnsToShowWhenListHidden: array of Boolean);
var
I: Integer;
begin
Assert(Length(aColumns) = Length(aColumnsToShowWhenListHidden));
fList.SetColumns(aFont, aColumns, aColumnOffsets);
SetLength(fColumnsToShowWhenListHidden, Length(aColumnsToShowWhenListHidden));
for I := Low(aColumnsToShowWhenListHidden) to High(aColumnsToShowWhenListHidden) do
fColumnsToShowWhenListHidden[I] := aColumnsToShowWhenListHidden[I];
end;
procedure TKMDropColumns.SetEnabled(aValue: Boolean);
begin
inherited;
fList.Enabled := Enabled;
end;
procedure TKMDropColumns.SetVisible(aValue: Boolean);
begin
inherited;
if not aValue then ListHide(Self);
end;
function TKMDropColumns.GetCount: Integer;
begin
Result := fList.RowCount;
end;
//When new items are added to the list we must update the drop height and position
procedure TKMDropColumns.UpdateDropPosition;
begin
if Count > 0 then
begin
fList.Height := Math.Min(fDropCount, fList.RowCount) * fList.ItemHeight + fList.Header.Height * Ord(fList.ShowHeader);
if fDropUp then
fList.AbsTop := AbsTop - fList.Height
else
fList.AbsTop := AbsTop + Height;
fList.Left := AbsLeft + Width - DropWidth - MasterParent.AbsLeft;
end;
end;
procedure TKMDropColumns.Add(aItem: TKMListRow);
begin
fList.AddItem(aItem);
end;
function TKMDropColumns.GetItem(aIndex: Integer): TKMListRow;
begin
Result := fList.Rows[aIndex];
end;
procedure TKMDropColumns.Clear;
begin
fList.Clear;
end;
procedure TKMDropColumns.Paint;
var
col: TColor4;
begin
inherited;
if Enabled then
col := icWhite
else
col := icGray2;
if ItemIndex = -1 then
TKMRenderUI.WriteText(AbsLeft + 4, AbsTop + 4, Width - 8 - fButton.Width, fDefaultCaption, fFont, taLeft, col)
else
fList.DoPaintLine(ItemIndex, AbsLeft, AbsTop, Width - fButton.Width, fColumnsToShowWhenListHidden, False);
end;
end.