mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-30 09:50:18 +02:00
LCL: Improve TTreeView mouse selection. Issue #37145
git-svn-id: branches/fixes_2_0@63475 -
This commit is contained in:
parent
2d62ff2977
commit
a4206958b2
@ -3279,7 +3279,8 @@ type
|
|||||||
tvoShowSeparators,
|
tvoShowSeparators,
|
||||||
tvoToolTips,
|
tvoToolTips,
|
||||||
tvoNoDoubleClickExpand,
|
tvoNoDoubleClickExpand,
|
||||||
tvoThemedDraw
|
tvoThemedDraw,
|
||||||
|
tvoEmptySpaceUnselect
|
||||||
);
|
);
|
||||||
TTreeViewOptions = set of TTreeViewOption;
|
TTreeViewOptions = set of TTreeViewOption;
|
||||||
|
|
||||||
@ -3332,7 +3333,6 @@ type
|
|||||||
FMaxLvl: integer; // maximum level of all nodes
|
FMaxLvl: integer; // maximum level of all nodes
|
||||||
FMaxRight: integer; // maximum text width of all nodes (needed for horizontal scrolling)
|
FMaxRight: integer; // maximum text width of all nodes (needed for horizontal scrolling)
|
||||||
FMouseDownPos: TPoint;
|
FMouseDownPos: TPoint;
|
||||||
FMouseDownNodeSelected: Boolean;
|
|
||||||
FMouseDownOnFoldingSign: Boolean;
|
FMouseDownOnFoldingSign: Boolean;
|
||||||
FMultiSelectStyle: TMultiSelectStyle;
|
FMultiSelectStyle: TMultiSelectStyle;
|
||||||
FHotTrackColor: TColor;
|
FHotTrackColor: TColor;
|
||||||
@ -3416,7 +3416,7 @@ type
|
|||||||
function IsStoredBackgroundColor: Boolean;
|
function IsStoredBackgroundColor: Boolean;
|
||||||
procedure HintMouseLeave(Sender: TObject);
|
procedure HintMouseLeave(Sender: TObject);
|
||||||
procedure ImageListChange(Sender: TObject);
|
procedure ImageListChange(Sender: TObject);
|
||||||
function MouseDownNode(X, Y: Integer): TTreeNode;
|
function NodeIsSelected(aNode: TTreeNode): Boolean;
|
||||||
procedure OnChangeTimer(Sender: TObject);
|
procedure OnChangeTimer(Sender: TObject);
|
||||||
procedure SetAutoExpand(Value: Boolean);
|
procedure SetAutoExpand(Value: Boolean);
|
||||||
procedure SetBackgroundColor(Value: TColor);
|
procedure SetBackgroundColor(Value: TColor);
|
||||||
@ -3624,6 +3624,7 @@ type
|
|||||||
procedure EraseBackground(DC: HDC); override;
|
procedure EraseBackground(DC: HDC); override;
|
||||||
function GetHitTestInfoAt(X, Y: Integer): THitTests;
|
function GetHitTestInfoAt(X, Y: Integer): THitTests;
|
||||||
function GetNodeAt(X, Y: Integer): TTreeNode;
|
function GetNodeAt(X, Y: Integer): TTreeNode;
|
||||||
|
function GetNodeWithExpandSignAt(X, Y: Integer): TTreeNode;
|
||||||
procedure GetInsertMarkAt(X, Y: Integer; out AnInsertMarkNode: TTreeNode;
|
procedure GetInsertMarkAt(X, Y: Integer; out AnInsertMarkNode: TTreeNode;
|
||||||
out AnInsertMarkType: TTreeViewInsertMarkType);
|
out AnInsertMarkType: TTreeViewInsertMarkType);
|
||||||
procedure SetInsertMark(AnInsertMarkNode: TTreeNode;
|
procedure SetInsertMark(AnInsertMarkNode: TTreeNode;
|
||||||
|
@ -2793,7 +2793,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
//select again
|
//select again
|
||||||
bGoNext := (FirstNode.Index <= Node.Index);
|
bGoNext := (FirstNode.AbsoluteIndex <= Node.AbsoluteIndex);
|
||||||
I := FirstNode;
|
I := FirstNode;
|
||||||
I.MultiSelected:=True;
|
I.MultiSelected:=True;
|
||||||
while (I<>Node) do
|
while (I<>Node) do
|
||||||
@ -3856,6 +3856,7 @@ procedure TCustomTreeView.SetMultiSelect(const AValue: Boolean);
|
|||||||
begin
|
begin
|
||||||
if MultiSelect <> AValue then
|
if MultiSelect <> AValue then
|
||||||
begin
|
begin
|
||||||
|
ClearSelection;
|
||||||
if AValue then
|
if AValue then
|
||||||
Include(FOptions,tvoAllowMultiselect)
|
Include(FOptions,tvoAllowMultiselect)
|
||||||
else
|
else
|
||||||
@ -3968,16 +3969,35 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TCustomTreeView.GetNodeAt(X, Y: Integer): TTreeNode;
|
function TCustomTreeView.GetNodeAt(X, Y: Integer): TTreeNode;
|
||||||
|
var
|
||||||
|
b: Boolean;
|
||||||
begin
|
begin
|
||||||
if (X >= BorderWidth) and (X < ClientWidth - BorderWidth) then
|
Result := GetNodeAtY(Y);
|
||||||
Result := GetNodeAtY(Y)
|
if Result = nil then Exit;
|
||||||
|
if tvoRowSelect in Options then // row select
|
||||||
|
b := (X < BorderWidth) or (X >= ClientWidth - BorderWidth)
|
||||||
else
|
else
|
||||||
|
b := (X < Result.DisplayStateIconLeft) or (X >= Result.DisplayTextRight);
|
||||||
|
if b then
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomTreeView.GetNodeWithExpandSignAt(X, Y: Integer): TTreeNode;
|
||||||
|
var
|
||||||
|
b: Boolean;
|
||||||
|
begin
|
||||||
|
Result := GetNodeAtY(Y);
|
||||||
|
if Result = nil then Exit;
|
||||||
|
if tvoRowSelect in Options then // row select
|
||||||
|
b := (X < BorderWidth) or (X >= ClientWidth - BorderWidth)
|
||||||
|
else // need to include DisplayExpandSignLeft
|
||||||
|
b := (X < Result.DisplayExpandSignLeft) or (X >= Result.DisplayTextRight);
|
||||||
|
if b then
|
||||||
Result := nil;
|
Result := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomTreeView.GetInsertMarkAt(X, Y: Integer;
|
procedure TCustomTreeView.GetInsertMarkAt(X, Y: Integer;
|
||||||
out AnInsertMarkNode: TTreeNode; out AnInsertMarkType: TTreeViewInsertMarkType
|
out AnInsertMarkNode: TTreeNode; out AnInsertMarkType: TTreeViewInsertMarkType);
|
||||||
);
|
|
||||||
var
|
var
|
||||||
ANode: TTreeNode;
|
ANode: TTreeNode;
|
||||||
NodeRect: TRect;
|
NodeRect: TRect;
|
||||||
@ -5558,18 +5578,17 @@ begin
|
|||||||
Result := FIndent >= 0;
|
Result := FIndent >= 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TCustomTreeView.MouseDownNode(X, Y: Integer): TTreeNode;
|
function TCustomTreeView.NodeIsSelected(aNode: TTreeNode): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := GetNodeAt(X, Y);
|
Result := Assigned(aNode) and
|
||||||
// Update the NodeSelected flag.
|
(aNode.Selected or ((tvoAllowMultiselect in Options) and aNode.MultiSelected));
|
||||||
FMouseDownNodeSelected := Assigned(Result) and
|
|
||||||
(Result.Selected or ((tvoAllowMultiselect in Options) and Result.MultiSelected));
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
procedure TCustomTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
X, Y: Integer);
|
X, Y: Integer);
|
||||||
var
|
var
|
||||||
CursorNode: TTreeNode;
|
CursorNode: TTreeNode;
|
||||||
|
CursorNdSelected: Boolean;
|
||||||
LogicalX: Integer;
|
LogicalX: Integer;
|
||||||
begin
|
begin
|
||||||
{$IFDEF VerboseDrag}
|
{$IFDEF VerboseDrag}
|
||||||
@ -5578,23 +5597,33 @@ begin
|
|||||||
FMouseDownPos := Point(X,Y);
|
FMouseDownPos := Point(X,Y);
|
||||||
FStates:=FStates-[tvsEditOnMouseUp,tvsSingleSelectOnMouseUp];
|
FStates:=FStates-[tvsEditOnMouseUp,tvsSingleSelectOnMouseUp];
|
||||||
|
|
||||||
CursorNode := MouseDownNode(X, Y);
|
CursorNode := GetNodeAt(X, Y);
|
||||||
|
CursorNdSelected := NodeIsSelected(CursorNode);
|
||||||
LogicalX:=X;
|
LogicalX:=X;
|
||||||
|
|
||||||
//change selection on right click
|
//change selection on right click
|
||||||
if (Button = mbRight) and RightClickSelect and//right click
|
if (Button = mbRight) and RightClickSelect and //right click
|
||||||
(([ssDouble, ssTriple, ssQuad] * Shift) = []) and//single or first of a multi click
|
(([ssDouble, ssTriple, ssQuad] * Shift) = []) and //single or first of a multi click
|
||||||
not AllowMultiSelectWithCtrl(Shift) and//only when CTRL is not pressed
|
not AllowMultiSelectWithCtrl(Shift) and //only when CTRL is not pressed
|
||||||
(CursorNode <> nil) and
|
(CursorNode <> nil)
|
||||||
(LogicalX >= CursorNode.DisplayStateIconLeft)//only after expand sign
|
|
||||||
then
|
then
|
||||||
begin
|
begin
|
||||||
|
if not (tvoRowSelect in Options) and
|
||||||
|
(tvoEmptySpaceUnselect in Options) and
|
||||||
|
(LogicalX >= CursorNode.DisplayStateIconLeft) and
|
||||||
|
(LogicalX > CursorNode.DisplayTextRight) then
|
||||||
|
ClearSelection
|
||||||
|
else
|
||||||
if not (tvoAllowMultiselect in Options) then
|
if not (tvoAllowMultiselect in Options) then
|
||||||
Selected := CursorNode
|
Selected := CursorNode
|
||||||
else
|
else
|
||||||
if not FMouseDownNodeSelected then
|
if not CursorNdSelected then
|
||||||
Items.SelectOnlyThis(CursorNode);
|
Items.SelectOnlyThis(CursorNode);
|
||||||
end;
|
end
|
||||||
|
else // empty space below last node
|
||||||
|
if (Button = mbRight) and RightClickSelect and (CursorNode = nil) and
|
||||||
|
(tvoEmptySpaceUnselect in Options) then
|
||||||
|
ClearSelection;
|
||||||
|
|
||||||
if not Focused and CanFocus then
|
if not Focused and CanFocus then
|
||||||
SetFocus;
|
SetFocus;
|
||||||
@ -5602,7 +5631,8 @@ begin
|
|||||||
inherited MouseDown(Button, Shift, X, Y);
|
inherited MouseDown(Button, Shift, X, Y);
|
||||||
|
|
||||||
//CursorNode must be reassigned again - e.g. in OnMouseDown the node can be deleted or moved.
|
//CursorNode must be reassigned again - e.g. in OnMouseDown the node can be deleted or moved.
|
||||||
CursorNode := MouseDownNode(X, Y);
|
CursorNode := GetNodeWithExpandSignAt(LogicalX, Y);
|
||||||
|
CursorNdSelected := NodeIsSelected(CursorNode);
|
||||||
|
|
||||||
//Flag is used for DblClick/TripleClick/QuadClick, so set it before testing ShiftState
|
//Flag is used for DblClick/TripleClick/QuadClick, so set it before testing ShiftState
|
||||||
FMouseDownOnFoldingSign :=
|
FMouseDownOnFoldingSign :=
|
||||||
@ -5611,14 +5641,14 @@ begin
|
|||||||
(LogicalX < CursorNode.DisplayExpandSignRight);
|
(LogicalX < CursorNode.DisplayExpandSignRight);
|
||||||
|
|
||||||
//change selection on left click
|
//change selection on left click
|
||||||
if (Button = mbLeft) and//left click
|
if (Button = mbLeft) and //left click
|
||||||
(([ssDouble, ssTriple, ssQuad] * Shift) = []) and//single or first of a multi click
|
(([ssDouble, ssTriple, ssQuad] * Shift) = []) and //single or first of a multi click
|
||||||
(CursorNode <> nil) then
|
(CursorNode <> nil) then
|
||||||
begin
|
begin
|
||||||
if FMouseDownOnFoldingSign then
|
if FMouseDownOnFoldingSign then
|
||||||
// mousedown occured on expand sign -> expand/collapse
|
// mousedown occured on expand sign -> expand/collapse
|
||||||
CursorNode.Expanded := not CursorNode.Expanded
|
CursorNode.Expanded := not CursorNode.Expanded
|
||||||
else if LogicalX >= CursorNode.DisplayStateIconLeft then
|
else if (LogicalX >= CursorNode.DisplayStateIconLeft) or (tvoRowSelect in Options) then
|
||||||
begin
|
begin
|
||||||
// mousedown occured in text or icon -> select node and begin drag operation
|
// mousedown occured in text or icon -> select node and begin drag operation
|
||||||
{$IFDEF VerboseDrag}
|
{$IFDEF VerboseDrag}
|
||||||
@ -5649,18 +5679,24 @@ begin
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
if not FMouseDownNodeSelected then
|
if not CursorNdSelected then
|
||||||
Items.SelectOnlyThis(CursorNode)
|
Items.SelectOnlyThis(CursorNode)
|
||||||
else
|
else
|
||||||
Include(FStates, tvsSingleSelectOnMouseUp);
|
Include(FStates, tvsSingleSelectOnMouseUp);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end
|
||||||
|
else if tvoEmptySpaceUnselect in Options then
|
||||||
|
ClearSelection;
|
||||||
end
|
end
|
||||||
else// multi click
|
else// multi click
|
||||||
if not (tvoNoDoubleClickExpand in Options) and (ssDouble in Shift)
|
if not (tvoNoDoubleClickExpand in Options) and (ssDouble in Shift)
|
||||||
and (Button = mbLeft) and (CursorNode<>nil) then
|
and (Button = mbLeft) and (CursorNode<>nil) then
|
||||||
CursorNode.Expanded := not CursorNode.Expanded;
|
CursorNode.Expanded := not CursorNode.Expanded
|
||||||
|
else // empty space below last node
|
||||||
|
if (Button = mbLeft) and (CursorNode = nil) and (tvoEmptySpaceUnselect in Options) and
|
||||||
|
not AllowMultiSelectWithShift(Shift) and not AllowMultiSelectWithCtrl(Shift) then
|
||||||
|
ClearSelection;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomTreeView.MouseMove(Shift: TShiftState; X, Y: Integer);
|
procedure TCustomTreeView.MouseMove(Shift: TShiftState; X, Y: Integer);
|
||||||
|
Loading…
Reference in New Issue
Block a user