unit Myc.Fmx.AstEditor.Handlers; interface uses System.SysUtils, System.Generics.Collections, System.UITypes, Myc.Ast.Nodes, Myc.Ast.Identities, Myc.Fmx.AstEditor.Render, Myc.Fmx.AstEditor.Node; type // --- Abstract List Base --- // Implements IReorderable to support drag & drop reordering within the virtual tree TNodeListHandler = class(TBaseNodeHandler>, IReorderable) protected FChildNodes: TList; function GetOrientation: TLayoutOrientation; virtual; abstract; function GetAlignment: TLayoutAlignment; virtual; public constructor Create(const ANode: INodeList); destructor Destroy; override; procedure BuildUI(OwnerNode: TAstViewNode); override; function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override; abstract; // IReorderable Implementation function CanDrag(Child: TVisualNode): Boolean; procedure MoveChild(SourceIndex, TargetIndex: Integer); end; implementation { TNodeListHandler } constructor TNodeListHandler.Create(const ANode: INodeList); begin inherited Create(ANode); FChildNodes := TList.Create; end; destructor TNodeListHandler.Destroy; begin FChildNodes.Free; inherited; end; function TNodeListHandler.GetAlignment: TLayoutAlignment; begin Result := TLayoutAlignment.laCenter; end; procedure TNodeListHandler.BuildUI(OwnerNode: TAstViewNode); var visu: IAstVisualizer; i: Integer; childView: TAstViewNode; openTok, closeTok, sep: string; listId: IListIdentity; begin if (FNode.Identity <> nil) and (FNode.Identity.Kind = ikList) then begin listId := FNode.Identity.AsList; openTok := listId.OpenToken; closeTok := listId.CloseToken; sep := listId.Separator; end else begin openTok := ''; closeTok := ''; sep := ' '; end; visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth); // No BeginUpdate/EndUpdate needed for TVisualNode, just property setting OwnerNode.Frameless := True; OwnerNode.Orientation := GetOrientation; OwnerNode.Alignment := GetAlignment; if openTok <> '' then OwnerNode.AddLabel(OwnerNode, openTok); for i := 0 to FNode.Count - 1 do begin childView := visu.CallAccept(FNode[i]); FChildNodes.Add(childView); if (i < FNode.Count - 1) and (sep <> '') and (sep <> ' ') then OwnerNode.AddLabel(OwnerNode, sep); end; if closeTok <> '' then OwnerNode.AddLabel(OwnerNode, closeTok); if FNode.Count = 0 then begin if (openTok = '') and (closeTok = '') then begin var lbl := OwnerNode.AddLabel(OwnerNode, 'ø'); lbl.Opacity := 0.5; end; end; end; // IReorderable Implementation function TNodeListHandler.CanDrag(Child: TVisualNode): Boolean; begin // Only allow dragging if the child is one of our managed ViewNodes if Child is TAstViewNode then Result := FChildNodes.Contains(TAstViewNode(Child)) else Result := False; end; procedure TNodeListHandler.MoveChild(SourceIndex, TargetIndex: Integer); var item: TAstViewNode; begin if (SourceIndex < 0) or (SourceIndex >= FChildNodes.Count) then Exit; if (TargetIndex < 0) or (TargetIndex > FChildNodes.Count) then Exit; // Adjust target index if moving downwards (since removing source shifts indices) if SourceIndex < TargetIndex then Dec(TargetIndex); if SourceIndex = TargetIndex then Exit; item := FChildNodes[SourceIndex]; FChildNodes.Delete(SourceIndex); FChildNodes.Insert(TargetIndex, item); // The visual order update happens via the controller's compilation cycle end; end.