LCL: FilterEdit support for PageUp, PageDown, Ctrl+Home, Ctrl+End and basic support for selection. Issue #28900

git-svn-id: trunk@50164 -
This commit is contained in:
ondrej 2015-10-25 20:47:35 +00:00
parent c2ae186c25
commit 471e8b0da4
6 changed files with 401 additions and 133 deletions

View File

@ -17,7 +17,8 @@ interface
uses
Classes, SysUtils, Forms, LResources, Graphics, Controls, StdCtrls,
LCLProc, LCLType, EditBtn, CheckLst, LazFileUtils, LazUTF8, AvgLvlTree;
LCLProc, LCLType, EditBtn, CheckLst, LazFileUtils, LazUTF8, AvgLvlTree,
Math;
type
@ -38,8 +39,14 @@ type
function CompareFNs(AFilename1,AFilename2: string): integer;
procedure SetFilteredListbox(const AValue: TCustomListBox);
protected
procedure MoveNext; override;
procedure MovePrev; override;
procedure EditEnter; override;
procedure MoveTo(AIndex: Integer; ASelect: Boolean);
procedure MoveNext(ASelect: Boolean = False); override;
procedure MovePrev(ASelect: Boolean = False); override;
procedure MovePageUp(ASelect: Boolean = False); override;
procedure MovePageDown(ASelect: Boolean = False); override;
procedure MoveHome(ASelect: Boolean = False); override;
procedure MoveEnd(ASelect: Boolean = False); override;
function ReturnKeyHandled: Boolean; override;
procedure SortAndFilter; override;
procedure ApplyFilterCore; override;
@ -91,6 +98,14 @@ begin
inherited Destroy;
end;
procedure TListFilterEdit.EditEnter;
begin
inherited EditEnter;
Exit;
if (fFilteredListbox.SelCount = 0) and (fFilteredListbox.Count > 0) then
fFilteredListbox.Selected[0] := True;
end;
procedure TListFilterEdit.RemoveItem(AItem: string);
var
i: Integer;
@ -111,6 +126,18 @@ begin
fCheckedItems.Remove(AItem);
end;
procedure TListFilterEdit.MoveEnd(ASelect: Boolean);
begin
if fFilteredListbox.Items.Count > 0 then
MoveTo(fFilteredListbox.Items.Count-1, ASelect);
end;
procedure TListFilterEdit.MoveHome(ASelect: Boolean);
begin
if fFilteredListbox.Items.Count > 0 then
MoveTo(0, ASelect);
end;
function TListFilterEdit.GetDefaultGlyph: TBitmap;
begin
Result := ListFilterGlyph;
@ -223,28 +250,89 @@ begin
end;
end;
procedure TListFilterEdit.MoveNext;
procedure TListFilterEdit.MoveNext(ASelect: Boolean);
var
i: Integer;
begin
if fFilteredListbox.Count = 0 then Exit;
i := fFilteredListbox.ItemIndex + 1;
if i < fFilteredListbox.Count then
fFilteredListbox.ItemIndex := i
if (fFilteredListbox.ItemIndex=0) and not fFilteredListbox.Selected[0] then
i := 0
else
fFilteredListbox.ItemIndex := 0;
i := fFilteredListbox.ItemIndex + 1;
if i >= fFilteredListbox.Count then
i := fFilteredListbox.Count-1;
MoveTo(i, ASelect);
end;
procedure TListFilterEdit.MovePrev;
procedure TListFilterEdit.MovePageDown(ASelect: Boolean);
var
I: Integer;
begin
if fFilteredListbox.Items.Count = 0 then
Exit;
I := fFilteredListbox.ItemIndex + Pred(fFilteredListbox.ClientHeight div fFilteredListbox.ItemHeight);
if (I < 0) or (I >= fFilteredListbox.Items.Count) then
I := fFilteredListbox.Items.Count-1;
MoveTo(I, ASelect);
end;
procedure TListFilterEdit.MovePageUp(ASelect: Boolean);
var
I: Integer;
begin
if fFilteredListbox.Items.Count = 0 then
Exit;
I := fFilteredListbox.ItemIndex - Pred(fFilteredListbox.ClientHeight div fFilteredListbox.ItemHeight);
if (I < 0) or (I >= fFilteredListbox.Items.Count) then
I := 0;
MoveTo(I, ASelect);
end;
procedure TListFilterEdit.MovePrev(ASelect: Boolean);
var
i: Integer;
begin
if fFilteredListbox.Count = 0 then Exit;
i := fFilteredListbox.ItemIndex - 1;
if i >= 0 then
fFilteredListbox.ItemIndex := i
else
fFilteredListbox.ItemIndex := fFilteredListbox.Count-1;
if i < 0 then
i := 0;
MoveTo(i, ASelect);
end;
procedure TListFilterEdit.MoveTo(AIndex: Integer; ASelect: Boolean);
var
I: Integer;
begin
fFilteredListbox.LockSelectionChange;
fFilteredListbox.Items.BeginUpdate;
try
if ASelect and fFilteredListbox.MultiSelect then
begin
if fFilteredListbox.ItemIndex < AIndex then
for I := Max(0, fFilteredListbox.ItemIndex) to AIndex do
fFilteredListbox.Selected[I] := True
else
for I := Max(0, fFilteredListbox.ItemIndex) downto AIndex do
fFilteredListbox.Selected[I] := True
end else
begin
fFilteredListbox.ClearSelection;
fFilteredListbox.Selected[AIndex] := True;
end;
if not fFilteredListbox.ItemFullyVisible(AIndex) then
begin
if fFilteredListbox.TopIndex < AIndex then
fFilteredListbox.TopIndex := AIndex - Pred(fFilteredListbox.ClientHeight div fFilteredListbox.ItemHeight)
else
fFilteredListbox.TopIndex := AIndex;
end;
finally
fFilteredListbox.UnlockSelectionChange;
fFilteredListbox.Items.EndUpdate;
end;
end;
function TListFilterEdit.ReturnKeyHandled: Boolean;

View File

@ -17,7 +17,7 @@ interface
uses
Classes, SysUtils, Forms, LResources, Graphics, Controls, ComCtrls,
LCLProc, LCLType, EditBtn, FileUtil, LazUTF8, fgl;
LCLProc, LCLType, EditBtn, FileUtil, LazUTF8, fgl, Math;
type
@ -43,8 +43,14 @@ type
function MatchesFilter(aData: TStringArray): Boolean;
procedure SetFilteredListview(const AValue: TCustomListView);
protected
procedure MoveNext; override;
procedure MovePrev; override;
procedure MoveTo(AIndex: Integer; ASelect: Boolean);
function GetLastSelectedIndex: Integer;
procedure MoveNext(ASelect: Boolean = False); override;
procedure MovePrev(ASelect: Boolean = False); override;
procedure MovePageUp(ASelect: Boolean = False); override;
procedure MovePageDown(ASelect: Boolean = False); override;
procedure MoveHome(ASelect: Boolean = False); override;
procedure MoveEnd(ASelect: Boolean = False); override;
function ReturnKeyHandled: Boolean; override;
procedure SortAndFilter; override;
procedure ApplyFilterCore; override;
@ -97,6 +103,14 @@ begin
Result := ListFilterGlyph;
end;
function TListViewFilterEdit.GetLastSelectedIndex: Integer;
begin
if fFilteredListview.LastSelected<>nil then
Result := fFilteredListview.LastSelected.Index
else
Result := -1;
end;
function ListItem2Data(AItem: TListItem): TStringArray;
var
i: Integer;
@ -145,6 +159,20 @@ begin
Result := False;
end;
procedure TListViewFilterEdit.MoveEnd(ASelect: Boolean);
begin
if fFilteredListview.Items.Count = 0 then
Exit;
MoveTo(fFilteredListview.Items.Count-1, ASelect);
end;
procedure TListViewFilterEdit.MoveHome(ASelect: Boolean);
begin
if fFilteredListview.Items.Count = 0 then
Exit;
MoveTo(0, ASelect);
end;
procedure TListViewFilterEdit.SortAndFilter;
// Copy data from fOriginalData to fSortedData in sorted order
var
@ -196,29 +224,77 @@ begin
fFilteredListview.Items[i].Selected:=True;
end;
procedure TListViewFilterEdit.MoveNext;
procedure TListViewFilterEdit.MoveNext(ASelect: Boolean);
var
i: Integer;
begin
i := fFilteredListview.ItemIndex + 1;
if fFilteredListview.Items.Count > 0 then begin
if i < fFilteredListview.Items.Count then
fFilteredListview.ItemIndex := i
else
fFilteredListview.ItemIndex := 0;
end;
if fFilteredListview.Items.Count = 0 then Exit;
i := GetLastSelectedIndex + 1;
if i >= fFilteredListview.Items.Count then
i := fFilteredListview.Items.Count-1;
MoveTo(i, ASelect);
end;
procedure TListViewFilterEdit.MovePrev;
procedure TListViewFilterEdit.MovePageDown(ASelect: Boolean);
var
I: Integer;
begin
if fFilteredListview.Items.Count = 0 then
Exit;
I := GetLastSelectedIndex + fFilteredListview.VisibleRowCount;
if (I < 0) or (I >= fFilteredListview.Items.Count) then
I := fFilteredListview.Items.Count-1;
MoveTo(I, ASelect);
end;
procedure TListViewFilterEdit.MovePageUp(ASelect: Boolean);
var
I: Integer;
begin
if fFilteredListview.Items.Count = 0 then
Exit;
I := GetLastSelectedIndex - fFilteredListview.VisibleRowCount;
if (I < 0) or (I >= fFilteredListview.Items.Count) then
I := 0;
MoveTo(I, ASelect);
end;
procedure TListViewFilterEdit.MovePrev(ASelect: Boolean);
var
i: Integer;
begin
i := fFilteredListview.ItemIndex - 1;
if fFilteredListview.Items.Count > 0 then begin
if i >= 0 then
fFilteredListview.ItemIndex := i
else
fFilteredListview.ItemIndex := fFilteredListview.Items.Count-1;
if fFilteredListview.Items.Count = 0 then Exit;
i := GetLastSelectedIndex - 1;
if i < 0 then
i := 0;
MoveTo(i, ASelect);
end;
procedure TListViewFilterEdit.MoveTo(AIndex: Integer; ASelect: Boolean);
var
I: Integer;
begin
fFilteredListview.BeginUpdate;
try
if ASelect and fFilteredListview.MultiSelect then
begin
if fFilteredListview.ItemIndex < AIndex then
for I := Max(0, fFilteredListview.ItemIndex) to AIndex do
fFilteredListview.Items[I].Selected := True
else
for I := Max(0, fFilteredListview.ItemIndex) downto AIndex do
fFilteredListview.Items[I].Selected := True;
fFilteredListview.ItemIndex := AIndex;
end else
begin
fFilteredListview.ClearSelection;
fFilteredListview.ItemIndex := AIndex;
end;
if fFilteredListview.LastSelected<>nil then
fFilteredListview.LastSelected.MakeVisible(False);
finally
fFilteredListview.EndUpdate;
end;
end;

View File

@ -79,8 +79,12 @@ type
procedure OnBeforeTreeDestroy(Sender: TObject);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure MoveNext; override;
procedure MovePrev; override;
procedure MoveNext(ASelect: Boolean = False); override;
procedure MovePrev(ASelect: Boolean = False); override;
procedure MovePageUp(ASelect: Boolean = False); override;
procedure MovePageDown(ASelect: Boolean = False); override;
procedure MoveHome(ASelect: Boolean = False); override;
procedure MoveEnd(ASelect: Boolean = False); override;
function ReturnKeyHandled: Boolean; override;
procedure SortAndFilter; override;
procedure ApplyFilterCore; override;
@ -578,6 +582,16 @@ begin
end;
end;
procedure TTreeFilterEdit.MoveEnd(ASelect: Boolean);
begin
fFilteredTreeview.MoveEnd(ASelect);
end;
procedure TTreeFilterEdit.MoveHome(ASelect: Boolean);
begin
fFilteredTreeview.MoveHome(ASelect);
end;
function TTreeFilterEdit.GetCleanBranch(ARootNode: TTreeNode): TTreeFilterBranch;
// Get a new or existing branch with data cleared for a given tree-node.
begin
@ -607,14 +621,24 @@ begin
end;
end;
procedure TTreeFilterEdit.MoveNext;
procedure TTreeFilterEdit.MoveNext(ASelect: Boolean);
begin
fFilteredTreeview.MoveToNextNode;
fFilteredTreeview.MoveToNextNode(ASelect);
end;
procedure TTreeFilterEdit.MovePrev;
procedure TTreeFilterEdit.MovePageDown(ASelect: Boolean);
begin
fFilteredTreeview.MoveToPrevNode;
fFilteredTreeview.MovePageDown(ASelect);
end;
procedure TTreeFilterEdit.MovePageUp(ASelect: Boolean);
begin
fFilteredTreeview.MovePageUp(ASelect);
end;
procedure TTreeFilterEdit.MovePrev(ASelect: Boolean);
begin
fFilteredTreeview.MoveToPrevNode(ASelect);
end;
function TTreeFilterEdit.ReturnKeyHandled: Boolean;

View File

@ -1600,6 +1600,7 @@ type
property RowSelect: Boolean index Ord(lvpRowSelect) read GetProperty write SetProperty default False;
property SelCount: Integer read GetSelCount;
property Selected: TListItem read GetSelection write SetSelection;
property LastSelected: TListItem read FSelected;
property TabStop default true;
property TopItem: TListItem read GetTopItem;
property ViewOrigin: TPoint read GetViewOrigin write SetViewOrigin;
@ -3342,6 +3343,7 @@ type
function IsNodeVisible(ANode: TTreeNode): Boolean;
function IsNodeHeightFullVisible(ANode: TTreeNode): Boolean;
function IsInsertMarkVisible: boolean; virtual;
procedure MoveSelection(ANewNode: TTreeNode; ASelect: Boolean);
procedure Change(Node: TTreeNode); virtual;
procedure Collapse(Node: TTreeNode); virtual;
procedure CreateWnd; override;
@ -3481,8 +3483,12 @@ type
procedure ClearInvisibleSelection;
function StoreCurrentSelection: TStringList;
procedure ApplyStoredSelection(ASelection: TStringList; FreeList: boolean = True);
procedure MoveToNextNode;
procedure MoveToPrevNode;
procedure MoveToNextNode(ASelect: Boolean = False);
procedure MoveToPrevNode(ASelect: Boolean = False);
procedure MovePageDown(ASelect: Boolean = False);
procedure MovePageUp(ASelect: Boolean = False);
procedure MoveHome(ASelect: Boolean = False);
procedure MoveEnd(ASelect: Boolean = False);
public
property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor default clWindow;
property BorderWidth default 0;

View File

@ -449,8 +449,12 @@ type
procedure SortAndFilter; virtual; abstract;
procedure ApplyFilter(Immediately: Boolean = False);
procedure ApplyFilterCore; virtual; abstract;
procedure MoveNext; virtual; abstract;
procedure MovePrev; virtual; abstract;
procedure MoveNext(ASelect: Boolean = False); virtual; abstract;
procedure MovePrev(ASelect: Boolean = False); virtual; abstract;
procedure MovePageUp(ASelect: Boolean = False); virtual; abstract;
procedure MovePageDown(ASelect: Boolean = False); virtual; abstract;
procedure MoveHome(ASelect: Boolean = False); virtual; abstract;
procedure MoveEnd(ASelect: Boolean = False); virtual; abstract;
function ReturnKeyHandled: Boolean; virtual; abstract;
function GetDefaultGlyphName: String; override;
public
@ -2153,10 +2157,25 @@ begin
Handled:=False;
if Shift = [] then
case Key of
VK_UP: begin MovePrev; Handled:=True; end;
VK_DOWN: begin MoveNext; Handled:=True; end;
VK_RETURN: Handled:=ReturnKeyHandled;
end;
if (Shift = []) or (Shift = [ssShift]) then
begin
case Key of
VK_UP: begin MovePrev(ssShift in Shift); Handled:=True; end;
VK_DOWN: begin MoveNext(ssShift in Shift); Handled:=True; end;
VK_PRIOR: begin MovePageUp(ssShift in Shift); Handled:=True; end;
VK_NEXT: begin MovePageDown(ssShift in Shift); Handled:=True; end;
end;
end;
if (Shift = [ssCtrl]) or (Shift = [ssCtrl, ssShift]) then
begin
case Key of
VK_HOME: begin MoveHome(ssShift in Shift); Handled:=True; end;
VK_END: begin MoveEnd(ssShift in Shift); Handled:=True; end;
end;
end;
if Handled then
Key:=VK_UNKNOWN
else

View File

@ -3945,37 +3945,59 @@ const
EditKey = VK_F2;
EditKeyShift = [];
var
I: Integer;
lNode, tempNode: TTreeNode;
lNode: TTreeNode;
begin
inherited KeyDown(Key, Shift);
if (tvoAllowMultiSelect in FOptions) and (ssShift in Shift) then
lNode := FTreeNodes.FLastMultiSelected
else
lNode := Selected;
case Key of
VK_DOWN:
begin
if lNode <> nil then
lNode := lNode.GetNextExpanded
else if Items.Count > 0 then
lNode := FTreeNodes.GetFirstNode;
MoveToNextNode(ssShift in Shift);
Key:=VK_UNKNOWN;
end;
VK_UP:
begin
if lNode <> nil then
lNode := lNode.GetPrevExpanded
else if Items.Count > 0 then
lNode := Items.GetLastExpandedSubNode;
MoveToPrevNode(ssShift in Shift);
Key:=VK_UNKNOWN;
end;
VK_LEFT:
VK_HOME:
begin
MoveHome(ssShift in Shift);
Key:=VK_UNKNOWN;
end;
VK_END:
begin
MoveEnd(ssShift in Shift);
Key:=VK_UNKNOWN;
end;
VK_PRIOR: // Page Up
begin
MovePageUp(ssShift in Shift);
Key:=VK_UNKNOWN;
end;
VK_NEXT: // Page Down
begin
MovePageDown(ssShift in Shift);
Key:=VK_UNKNOWN;
end;
else
if not (Key in [VK_LEFT,VK_RIGHT,VK_ADD,VK_SUBTRACT]) then
Exit;
if (tvoAllowMultiSelect in FOptions) and (ssShift in Shift) then
lNode := FTreeNodes.FLastMultiSelected
else
lNode := Selected;
case Key of
VK_LEFT:
if lNode <> nil then
begin
if lNode.Expanded then
@ -3986,7 +4008,7 @@ begin
Key:=VK_UNKNOWN;
end;
VK_RIGHT:
VK_RIGHT:
if lNode <> nil then
begin
if lNode.Expanded then
@ -3996,74 +4018,23 @@ begin
Key:=VK_UNKNOWN;
end;
VK_ADD:
VK_ADD:
if lNode <> nil then
lNode.Expanded := True;
VK_SUBTRACT:
VK_SUBTRACT:
if lNode <> nil then
lNode.Expanded := False;
VK_HOME:
begin
tempNode := Items.GetFirstVisibleNode;
if tempNode<>nil then
lNode := tempNode;
else
if (Key=EditKey) and (Shift=EditKeyShift) and (not ReadOnly) then
BeginEditing(Selected);
lNode := nil; { No change in selection }
end;
VK_END:
begin
tempNode := Items.GetLastExpandedSubNode;
if tempNode<>nil then
lNode := tempNode;
end;
VK_PRIOR: // Page Up
if lNode <> nil then
begin
I := Pred(ClientHeight div DefaultItemHeight);
while (I > 0) do
if lNode.GetPrevExpanded <> nil then
begin
lNode := lNode.GetPrevExpanded;
Dec(I);
end
else Break;
end;
VK_NEXT: // Page Down
if lNode <> nil then
begin
I := Pred(ClientHeight div DefaultItemHeight);
while (I > 0) do
if lNode.GetNextExpanded <> nil then
begin
lNode := lNode.GetNextExpanded;
Dec(I);
end
else Break;
end;
else
if (Key=EditKey) and (Shift=EditKeyShift) and (not ReadOnly) then
BeginEditing(Selected);
lNode := nil; { No change in selection }
MoveSelection(lNode, ([ssShift] * Shift) <> []);
end;
if lNode <> nil then
if tvoAllowMultiSelect in FOptions then
begin
if ([ssCtrl,ssShift] * Shift) <> [] then
lNode.MultiSelectGroup
else begin
FTreeNodes.SelectOnlyThis(lNode);
end;
end else
Selected := lNode;
end;
procedure TCustomTreeView.Loaded;
@ -5334,6 +5305,96 @@ begin
tvsEditOnMouseUp,tvsSingleSelectOnMouseUp];
end;
procedure TCustomTreeView.MoveEnd(ASelect: Boolean);
var
lNode: TTreeNode;
begin
lNode := Items.GetLastExpandedSubNode;
if lNode <> nil then
MoveSelection(lNode, ASelect);
end;
procedure TCustomTreeView.MoveHome(ASelect: Boolean);
var
lNode: TTreeNode;
begin
lNode := Items.GetFirstVisibleNode;
if lNode <> nil then
MoveSelection(lNode, ASelect);
end;
procedure TCustomTreeView.MovePageDown(ASelect: Boolean);
var
I: Integer;
lNode: TTreeNode;
begin
if tvoAllowMultiSelect in FOptions then
lNode := FTreeNodes.FLastMultiSelected
else
lNode := Selected;
if lNode = nil then
lNode := Items.GetFirstVisibleNode;
if lNode <> nil then
begin
I := Pred(ClientHeight div DefaultItemHeight);
while (I > 0) do
if lNode.GetNextExpanded <> nil then
begin
lNode := lNode.GetNextExpanded;
Dec(I);
end
else Break;
end;
if lNode <> nil then
MoveSelection(lNode, ASelect);
end;
procedure TCustomTreeView.MovePageUp(ASelect: Boolean);
var
I: Integer;
lNode: TTreeNode;
begin
if tvoAllowMultiSelect in FOptions then
lNode := FTreeNodes.FLastMultiSelected
else
lNode := Selected;
if lNode = nil then
lNode := Items.GetFirstVisibleNode;
if lNode <> nil then
begin
I := Pred(ClientHeight div DefaultItemHeight);
while (I > 0) do
if lNode.GetPrevExpanded <> nil then
begin
lNode := lNode.GetPrevExpanded;
Dec(I);
end
else Break;
end;
if lNode <> nil then
MoveSelection(lNode, ASelect);
end;
procedure TCustomTreeView.MoveSelection(ANewNode: TTreeNode; ASelect: Boolean);
begin
if tvoAllowMultiSelect in FOptions then
begin
if ASelect then
ANewNode.MultiSelectGroup
else begin
FTreeNodes.SelectOnlyThis(ANewNode);
end;
end else
Selected := ANewNode;
ANewNode.MakeVisible;
end;
procedure TCustomTreeView.MouseLeave;
begin
FStates:=FStates-[tvsDblClicked,tvsTripleClicked,tvsQuadClicked,
@ -5850,7 +5911,7 @@ begin
end;
end;
procedure TCustomTreeView.MoveToNextNode;
procedure TCustomTreeView.MoveToNextNode(ASelect: Boolean);
var
ANode: TTreeNode;
begin
@ -5859,17 +5920,14 @@ begin
else
ANode := Selected;
if ANode <> nil then
ANode := ANode.GetNextVisible;
if (ANode = nil) and (Items.Count > 0) then
ANode := ANode.GetNextVisible
else
ANode := FTreeNodes.GetFirstVisibleNode;
if ANode <> nil then
if tvoAllowMultiSelect in FOptions then
FTreeNodes.SelectOnlyThis(ANode)
else
Selected := ANode;
MoveSelection(ANode, ASelect);
end;
procedure TCustomTreeView.MoveToPrevNode;
procedure TCustomTreeView.MoveToPrevNode(ASelect: Boolean);
var
ANode: TTreeNode;
begin
@ -5878,14 +5936,11 @@ begin
else
ANode := Selected;
if ANode <> nil then
ANode := ANode.GetPrevVisible;
if (ANode = nil) and (Items.Count > 0) then
ANode := Items.GetLastExpandedSubNode;
ANode := ANode.GetPrevVisible
else
ANode := Items.GetFirstVisibleNode;
if ANode <> nil then
if tvoAllowMultiSelect in FOptions then
FTreeNodes.SelectOnlyThis(ANode)
else
Selected := ANode;
MoveSelection(ANode, ASelect);
end;
function TCustomTreeView.StoreCurrentSelection: TStringList;