From 257f842d30119bf37638d846cb5d53a2157003bd Mon Sep 17 00:00:00 2001 From: juha Date: Tue, 26 May 2020 23:24:01 +0000 Subject: [PATCH] LCL: Improve TTreeView mouse selection. Issue #37145, patch from Joeny Ang. git-svn-id: trunk@63232 - --- lcl/comctrls.pp | 6 ++-- lcl/include/treeview.inc | 70 ++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/lcl/comctrls.pp b/lcl/comctrls.pp index e96bff1871..e2b28c3245 100644 --- a/lcl/comctrls.pp +++ b/lcl/comctrls.pp @@ -3287,7 +3287,8 @@ type tvoShowSeparators, tvoToolTips, tvoNoDoubleClickExpand, - tvoThemedDraw + tvoThemedDraw, + tvoEmptySpaceUnselect ); TTreeViewOptions = set of TTreeViewOption; @@ -3340,7 +3341,6 @@ type FMaxLvl: integer; // maximum level of all nodes FMaxRight: integer; // maximum text width of all nodes (needed for horizontal scrolling) FMouseDownPos: TPoint; - FMouseDownNodeSelected: Boolean; FMouseDownOnFoldingSign: Boolean; FMultiSelectStyle: TMultiSelectStyle; FHotTrackColor: TColor; @@ -3425,7 +3425,7 @@ type function IsStoredBackgroundColor: Boolean; procedure HintMouseLeave(Sender: TObject); procedure ImageListChange(Sender: TObject); - function MouseDownNode(X, Y: Integer): TTreeNode; + function NodeIsSelected(aNode: TTreeNode): Boolean; procedure OnChangeTimer(Sender: TObject); procedure SetAutoExpand(Value: Boolean); procedure SetBackgroundColor(Value: TColor); diff --git a/lcl/include/treeview.inc b/lcl/include/treeview.inc index 1fb5603641..aba2606d0b 100644 --- a/lcl/include/treeview.inc +++ b/lcl/include/treeview.inc @@ -2797,7 +2797,7 @@ begin end; //select again - bGoNext := (FirstNode.Index <= Node.Index); + bGoNext := (FirstNode.AbsoluteIndex <= Node.AbsoluteIndex); I := FirstNode; I.MultiSelected:=True; while (I<>Node) do @@ -3861,6 +3861,7 @@ procedure TCustomTreeView.SetMultiSelect(const AValue: Boolean); begin if MultiSelect <> AValue then begin + ClearSelection; if AValue then Include(FOptions,tvoAllowMultiselect) else @@ -3974,15 +3975,19 @@ end; function TCustomTreeView.GetNodeAt(X, Y: Integer): TTreeNode; begin - if (X >= BorderWidth) and (X < ClientWidth - BorderWidth) then - Result := GetNodeAtY(Y) - else + Result := GetNodeAtY(Y); + if Result = nil then Exit; + if (not (tvoRowSelect in Options) and + ((X < Result.DisplayStateIconLeft) or (X >= Result.DisplayTextRight))) + or + ((tvoRowSelect in Options) and // row select + ((X < BorderWidth) or (X >= ClientWidth - BorderWidth))) + then Result := nil; end; procedure TCustomTreeView.GetInsertMarkAt(X, Y: Integer; - out AnInsertMarkNode: TTreeNode; out AnInsertMarkType: TTreeViewInsertMarkType - ); + out AnInsertMarkNode: TTreeNode; out AnInsertMarkType: TTreeViewInsertMarkType); var ANode: TTreeNode; NodeRect: TRect; @@ -5574,18 +5579,17 @@ begin Result := FIndent >= 0; end; -function TCustomTreeView.MouseDownNode(X, Y: Integer): TTreeNode; +function TCustomTreeView.NodeIsSelected(aNode: TTreeNode): Boolean; begin - Result := GetNodeAt(X, Y); - // Update the NodeSelected flag. - FMouseDownNodeSelected := Assigned(Result) and - (Result.Selected or ((tvoAllowMultiselect in Options) and Result.MultiSelected)); + Result := Assigned(aNode) and + (aNode.Selected or ((tvoAllowMultiselect in Options) and aNode.MultiSelected)); end; procedure TCustomTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var CursorNode: TTreeNode; + CursorNdSelected: Boolean; LogicalX: Integer; begin {$IFDEF VerboseDrag} @@ -5594,23 +5598,33 @@ begin FMouseDownPos := Point(X,Y); FStates:=FStates-[tvsEditOnMouseUp,tvsSingleSelectOnMouseUp]; - CursorNode := MouseDownNode(X, Y); + CursorNode := GetNodeAt(X, Y); + CursorNdSelected := NodeIsSelected(CursorNode); LogicalX:=X; //change selection on right click - if (Button = mbRight) and RightClickSelect and//right click - (([ssDouble, ssTriple, ssQuad] * Shift) = []) and//single or first of a multi click - not AllowMultiSelectWithCtrl(Shift) and//only when CTRL is not pressed - (CursorNode <> nil) and - (LogicalX >= CursorNode.DisplayStateIconLeft)//only after expand sign + if (Button = mbRight) and RightClickSelect and //right click + (([ssDouble, ssTriple, ssQuad] * Shift) = []) and //single or first of a multi click + not AllowMultiSelectWithCtrl(Shift) and //only when CTRL is not pressed + (CursorNode <> nil) then 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 Selected := CursorNode else - if not FMouseDownNodeSelected then + if not CursorNdSelected then 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 SetFocus; @@ -5618,7 +5632,8 @@ begin inherited MouseDown(Button, Shift, X, Y); //CursorNode must be reassigned again - e.g. in OnMouseDown the node can be deleted or moved. - CursorNode := MouseDownNode(X, Y); + CursorNode := GetNodeAt(LogicalX, Y); + CursorNdSelected := NodeIsSelected(CursorNode); //Flag is used for DblClick/TripleClick/QuadClick, so set it before testing ShiftState FMouseDownOnFoldingSign := @@ -5634,7 +5649,8 @@ begin if FMouseDownOnFoldingSign then // mousedown occured on expand sign -> expand/collapse CursorNode.Expanded := not CursorNode.Expanded - else if LogicalX >= CursorNode.DisplayStateIconLeft then + else if (LogicalX >= CursorNode.DisplayStateIconLeft) or + (tvoRowSelect in Options) then begin // mousedown occured in text or icon -> select node and begin drag operation {$IFDEF VerboseDrag} @@ -5665,18 +5681,24 @@ begin end else begin - if not FMouseDownNodeSelected then + if not CursorNdSelected then Items.SelectOnlyThis(CursorNode) else Include(FStates, tvsSingleSelectOnMouseUp); end; end; - end; + end + else if tvoEmptySpaceUnselect in Options then + ClearSelection; end else// multi click if not (tvoNoDoubleClickExpand in Options) and (ssDouble in Shift) 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; procedure TCustomTreeView.MouseMove(Shift: TShiftState; X, Y: Integer);