From 656375de99c972d402fce5309223f0f3a6785627 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Mon, 1 Dec 2025 13:19:07 +0100 Subject: [PATCH] Focus & cursor movement in UI --- ASTPlayground/ASTPlayground.delphilsp.json | 2 +- ASTPlayground/ASTPlayground.dpr | 6 +- ASTPlayground/ASTPlayground.dproj | 4 +- ASTPlayground/MainForm.pas | 70 +- Src/AST/Myc.Fmx.AstEditor.Controller.pas | 432 ------- Src/AST/Myc.Fmx.AstEditor.Handlers.pas | 2 +- Src/AST/Myc.Fmx.AstEditor.Node.pas | 542 +++++++++ Src/AST/Myc.Fmx.AstEditor.Visualizer.pas | 2 +- Src/AST/Myc.Fmx.AstEditor.Workspace.pas | 9 +- Src/AST/Myc.Fmx.AstEditor.pas | 1266 ++++++++++++++------ 10 files changed, 1468 insertions(+), 867 deletions(-) delete mode 100644 Src/AST/Myc.Fmx.AstEditor.Controller.pas create mode 100644 Src/AST/Myc.Fmx.AstEditor.Node.pas diff --git a/ASTPlayground/ASTPlayground.delphilsp.json b/ASTPlayground/ASTPlayground.delphilsp.json index 37b65e5..af95420 100644 --- a/ASTPlayground/ASTPlayground.delphilsp.json +++ b/ASTPlayground/ASTPlayground.delphilsp.json @@ -1 +1 @@ -{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.Nodes", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Nodes.pas" }, { "name": "Myc.Ast.Scope", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Scope.pas" }, { "name": "Myc.Data.Value", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Data.Value.pas" }, { "name": "Myc.Ast.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.pas" }, { "name": "Myc.Ast.RTL", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.pas" }, { "name": "Myc.Ast.Dumper", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Dumper.pas" }, { "name": "Myc.Ast.RTL.Core", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.Core.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.Types", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Types.pas" }, { "name": "Myc.Data.Keyword", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Keyword.pas" }, { "name": "Myc.Ast.Compiler.TCO", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TCO.pas" }, { "name": "Myc.Ast.Compiler.Binder", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Binder.pas" }, { "name": "Myc.Ast.Compiler.Lowering", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Lowering.pas" }, { "name": "Myc.Ast.Compiler.TypeChecker", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TypeChecker.pas" }, { "name": "Myc.Ast.Compiler.Macros", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Macros.pas" }, { "name": "Myc.Ast.Compiler.Specializer", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Specializer.pas" }, { "name": "Myc.Ast.Environment", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Environment.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Analysis.Purity", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Analysis.Purity.pas" }, { "name": "Myc.Ast.Identities", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Identities.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.pas" }, { "name": "Myc.Fmx.AstEditor.Visualizer", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Controller", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Controller.pas" }, { "name": "Myc.Fmx.AstEditor", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.pas" }, { "name": "Myc.Fmx.AstEditor.Layout", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Layout.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": false, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/FlatBox2D","file:///C%3A/Users/Public/Documents/Embarcadero/Studio/37.0/Dcp/%25PLATFORM%25","file:///%25TP%25/runtime/vcl","file:///S%3A/sc_famos_src/svn_vcl/cpxedits","file:///S%3A/sc_famos_src/svn_vcl/dzChart","file:///S%3A/sc_famos_src/svn_vcl/eaeditor","file:///S%3A/sc_famos_src/svn_vcl/FamosEvents","file:///S%3A/sc_famos_src/svn_vcl/HTML_Viewer/source","file:///S%3A/sc_famos_src/svn_vcl/Myc_ChartEdit","file:///S%3A/sc_famos_src/svn_vcl/Myc_HemiDrag","file:///S%3A/sc_famos_src/svn_vcl/Myc_SplitControl","file:///S%3A/sc_famos_src/svn_vcl/NiceChart","file:///S%3A/sc_famos_src/svn_vcl/plotter","file:///S%3A/sc_famos_src/svn_vcl/SimpleIPC","file:///S%3A/sc_famos_src/svn_vcl/SynEdit-master/Source","file:///S%3A/sc_famos_src/svn_vcl/ValEdits","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib/win32","file:///S%3A/sc_famos_src/svn_vcl/Virtual-TreeView/Source","file:///S%3A/sc_famos_src/svn_common/caHelpSys","file:///S%3A/sc_famos_src/svn_common/Misc","file:///S%3A/sc_famos_src/svn_common/Myc_VecMath","file:///S%3A/sc_famos_src/svn_common/PascalScript/source","file:///S%3A/sc_famos_src/svn_common/TreeBase","file:///S%3A/sc_famos_src/svn_common/TreeBase/Medien","file:///S%3A/3rdParty/spring4d/Source","file:///S%3A/3rdParty/spring4d/Source/Base","file:///S%3A/3rdParty/spring4d/Source/Base/Collections","file:///S%3A/3rdParty/spring4d/Source/Base/Logging","file:///S%3A/3rdParty/spring4d/Source/Base/Patches","file:///S%3A/3rdParty/spring4d/Source/Core/Container","file:///S%3A/3rdParty/spring4d/Source/Core/Interception","file:///S%3A/3rdParty/spring4d/Source/Core/Logging","file:///S%3A/3rdParty/spring4d/Source/Core/Mocking","file:///S%3A/3rdParty/spring4d/Source/Core/Services","file:///S%3A/3rdParty/spring4d/Source/Extensions/Cryptography","file:///S%3A/3rdParty/spring4d/Source/Extensions/Utils","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCommon%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressGDI%2B%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLibrary/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/XP%20Theme%20Manager/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLayout%20Control/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressPageControl/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressEditors%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDocking%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressBars/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressOfficeCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressMemData/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDataController/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressNavBar/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressSkins%20Library/Sources","file:///T%3A/Myc/Src","file:///T%3A/Myc/Src/Data","file:///T%3A/Myc/Src/AST" ] , "CommonAppData": "file:///C%3A/Users/Brummel/AppData/Roaming/Embarcadero/BDS/37.0/" , "Templates": "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/ObjRepos/" } } \ No newline at end of file +{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.Nodes", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Nodes.pas" }, { "name": "Myc.Ast.Scope", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Scope.pas" }, { "name": "Myc.Data.Value", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Data.Value.pas" }, { "name": "Myc.Ast.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.pas" }, { "name": "Myc.Ast.RTL", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.pas" }, { "name": "Myc.Ast.Dumper", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Dumper.pas" }, { "name": "Myc.Ast.RTL.Core", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.Core.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.Types", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Types.pas" }, { "name": "Myc.Data.Keyword", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Keyword.pas" }, { "name": "Myc.Ast.Compiler.TCO", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TCO.pas" }, { "name": "Myc.Ast.Compiler.Binder", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Binder.pas" }, { "name": "Myc.Ast.Compiler.Lowering", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Lowering.pas" }, { "name": "Myc.Ast.Compiler.TypeChecker", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TypeChecker.pas" }, { "name": "Myc.Ast.Compiler.Macros", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Macros.pas" }, { "name": "Myc.Ast.Compiler.Specializer", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Specializer.pas" }, { "name": "Myc.Ast.Environment", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Environment.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Analysis.Purity", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Analysis.Purity.pas" }, { "name": "Myc.Ast.Identities", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Identities.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.pas" }, { "name": "Myc.Fmx.AstEditor.Visualizer", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Layout", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Layout.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Fmx.AstEditor", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": true, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/FlatBox2D","file:///C%3A/Users/Public/Documents/Embarcadero/Studio/37.0/Dcp/%25PLATFORM%25","file:///%25TP%25/runtime/vcl","file:///S%3A/sc_famos_src/svn_vcl/cpxedits","file:///S%3A/sc_famos_src/svn_vcl/dzChart","file:///S%3A/sc_famos_src/svn_vcl/eaeditor","file:///S%3A/sc_famos_src/svn_vcl/FamosEvents","file:///S%3A/sc_famos_src/svn_vcl/HTML_Viewer/source","file:///S%3A/sc_famos_src/svn_vcl/Myc_ChartEdit","file:///S%3A/sc_famos_src/svn_vcl/Myc_HemiDrag","file:///S%3A/sc_famos_src/svn_vcl/Myc_SplitControl","file:///S%3A/sc_famos_src/svn_vcl/NiceChart","file:///S%3A/sc_famos_src/svn_vcl/plotter","file:///S%3A/sc_famos_src/svn_vcl/SimpleIPC","file:///S%3A/sc_famos_src/svn_vcl/SynEdit-master/Source","file:///S%3A/sc_famos_src/svn_vcl/ValEdits","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib/win32","file:///S%3A/sc_famos_src/svn_vcl/Virtual-TreeView/Source","file:///S%3A/sc_famos_src/svn_common/caHelpSys","file:///S%3A/sc_famos_src/svn_common/Misc","file:///S%3A/sc_famos_src/svn_common/Myc_VecMath","file:///S%3A/sc_famos_src/svn_common/PascalScript/source","file:///S%3A/sc_famos_src/svn_common/TreeBase","file:///S%3A/sc_famos_src/svn_common/TreeBase/Medien","file:///S%3A/3rdParty/spring4d/Source","file:///S%3A/3rdParty/spring4d/Source/Base","file:///S%3A/3rdParty/spring4d/Source/Base/Collections","file:///S%3A/3rdParty/spring4d/Source/Base/Logging","file:///S%3A/3rdParty/spring4d/Source/Base/Patches","file:///S%3A/3rdParty/spring4d/Source/Core/Container","file:///S%3A/3rdParty/spring4d/Source/Core/Interception","file:///S%3A/3rdParty/spring4d/Source/Core/Logging","file:///S%3A/3rdParty/spring4d/Source/Core/Mocking","file:///S%3A/3rdParty/spring4d/Source/Core/Services","file:///S%3A/3rdParty/spring4d/Source/Extensions/Cryptography","file:///S%3A/3rdParty/spring4d/Source/Extensions/Utils","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCommon%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressGDI%2B%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLibrary/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/XP%20Theme%20Manager/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLayout%20Control/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressPageControl/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressEditors%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDocking%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressBars/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressOfficeCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressMemData/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDataController/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressNavBar/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressSkins%20Library/Sources","file:///T%3A/Myc/Src","file:///T%3A/Myc/Src/Data","file:///T%3A/Myc/Src/AST" ] , "CommonAppData": "file:///C%3A/Users/Brummel/AppData/Roaming/Embarcadero/BDS/37.0/" , "Templates": "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/ObjRepos/" } } \ No newline at end of file diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr index 73bb958..81dc4df 100644 --- a/ASTPlayground/ASTPlayground.dpr +++ b/ASTPlayground/ASTPlayground.dpr @@ -29,9 +29,9 @@ uses Myc.Fmx.AstEditor.Handlers in '..\Src\AST\Myc.Fmx.AstEditor.Handlers.pas', Myc.Fmx.AstEditor.Visualizer in '..\Src\AST\Myc.Fmx.AstEditor.Visualizer.pas', Myc.Fmx.AstEditor.Workspace in '..\Src\AST\Myc.Fmx.AstEditor.Workspace.pas', - Myc.Fmx.AstEditor.Controller in '..\Src\AST\Myc.Fmx.AstEditor.Controller.pas', - Myc.Fmx.AstEditor in '..\Src\AST\Myc.Fmx.AstEditor.pas', - Myc.Fmx.AstEditor.Layout in '..\Src\AST\Myc.Fmx.AstEditor.Layout.pas'; + Myc.Fmx.AstEditor.Layout in '..\Src\AST\Myc.Fmx.AstEditor.Layout.pas', + Myc.Fmx.AstEditor.Node in '..\Src\AST\Myc.Fmx.AstEditor.Node.pas', + Myc.Fmx.AstEditor in '..\Src\AST\Myc.Fmx.AstEditor.pas'; {$R *.res} diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 2a24462..75a07ad 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -159,9 +159,9 @@ - - + + Base diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index ef802ec..bfaff17 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -44,9 +44,7 @@ uses Myc.Ast.RTL, Myc.Ast.Compiler.Macros, // Editor Units - Myc.Fmx.AstEditor, - Myc.Fmx.AstEditor.Workspace, - Myc.Fmx.AstEditor.Controller; // Controller integration + Myc.Fmx.AstEditor; // Die neue Komponente (ersetzt Controller und Workspace-Access) type // A test record @@ -93,7 +91,6 @@ type procedure ClearButtonClick(Sender: TObject); procedure CompilerStageBoxChange(Sender: TObject); procedure FormCreate(Sender: TObject); - procedure FormDestroy(Sender: TObject); procedure CreateTriggerExampleButtonClick(Sender: TObject); procedure DebugBoxChange(Sender: TObject); procedure DoTrigger2ButtonClick(Sender: TObject); @@ -119,17 +116,16 @@ type FCurrUnboundAst: IAstNode; FCurrExec: TCompiledFunction; FEnvironment: TAstEnvironment; - FWorkspace: TWorkspace; // Changed to TWorkspace - FController: TAstEditorController; // The UI Controller + FAstEditor: TAstEditor; // Die Komponente (Facade) FScriptUpdate: Boolean; FTriggerTest: TAstEnvironment; - procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); + procedure AstEditorMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); // Helper function to encapsulate the Compile -> Evaluate pattern function ExecuteAst(const ANode: IAstNode): TDataValue; procedure UpdateScript; - procedure ShowVizualization(X, Y: Single); + procedure ShowVizualization; procedure PrintScript(const Node: IAstNode); public { Public declarations } @@ -158,19 +154,20 @@ begin // 1. Initialize Environment FEnvironment := TAstEnvironment.Construct(TAst.CreateScope(nil)); - // 2. Initialize Workspace - // TWorkspace is now a simple viewport control - FWorkspace := TWorkspace.Create(Panel2); - FWorkspace.Parent := Panel2; - FWorkspace.Align := TAlignLayout.Client; - FWorkspace.ClipChildren := true; - FWorkspace.OnMouseDown := WorkspaceMouseDown; + // 2. Initialize AST Editor Component + // This replaces the manual Workspace + Controller setup. + // The Editor encapsulates logic for Navigation, Zoom, Drag&Drop, Undo/Redo. + FAstEditor := TAstEditor.Create(Self); + FAstEditor.Parent := Panel2; + FAstEditor.Align := TAlignLayout.Client; - // 3. Initialize Controller (connects Env and Workspace) - // The Controller will manage the nodes inside FWorkspace.World - FController := TAstEditorController.Create(FEnvironment, FWorkspace); + // Wire up events exposed by the component + FAstEditor.OnMouseDown := AstEditorMouseDown; - // 4. Register the SMA factory into the environment + // Initialize the logic backend of the editor + FAstEditor.Init(FEnvironment); + + // 3. Register the SMA factory into the environment var RegFunc := procedure(const Scope: IExecutionScope) var @@ -286,19 +283,13 @@ begin ClearButtonClick(Self); end; -procedure TForm1.FormDestroy(Sender: TObject); -begin - FController.Free; -end; - -procedure TForm1.ShowVizualization(X, Y: Single); +procedure TForm1.ShowVizualization; begin if FCurrUnboundAst = nil then exit; - // Use the Controller to build, validate and show the UI - // The controller internally clears FWorkspace.World and adds nodes there. - FController.SetRoot(FCurrUnboundAst); + // Use the Editor Component to build, validate and show the UI + FAstEditor.SetRoot(FCurrUnboundAst); end; // Simplified ExecuteAst using TEnvironment and handling errors gracefully @@ -422,14 +413,13 @@ end; procedure TForm1.ClearButtonClick(Sender: TObject); begin - // Clear content of world (not workspace itself, as it has the root layer) - FWorkspace.World.DeleteChildren; - FWorkspace.World.Repaint; + // Clear content of component + FAstEditor.SetRoot(nil); end; procedure TForm1.CompilerStageBoxChange(Sender: TObject); begin - ShowVizualization(14, 14); + ShowVizualization; end; procedure TForm1.CreateTriggerExampleButtonClick(Sender: TObject); @@ -731,8 +721,8 @@ begin var result := ExecuteAst(FCurrUnboundAst); Memo1.Lines.Add(Format('Script executed. Final result: %s', [result.ToString])); finally - // Trigger visualization update via Controller (inside ShowVizualization) - ShowVizualization(14, 14); + // Trigger visualization update via Component + ShowVizualization; end; except on E: Exception do @@ -890,15 +880,15 @@ procedure TForm1.UpdateScript; begin PrintScript(FCurrUnboundAst); - // Clear and redraw via Controller - FController.SetRoot(FCurrUnboundAst); + // Clear and redraw via Component + FAstEditor.SetRoot(FCurrUnboundAst); end; -procedure TForm1.WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); +procedure TForm1.AstEditorMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); begin - if Button <> TMouseButton.mbMiddle then - exit; - ShowVizualization(X, Y); + // Nur zur Demo: Wir könnten hier klicken, um etwas im Memo zu loggen + if Button = TMouseButton.mbMiddle then + ShowVizualization; end; procedure TForm1.SaveUserLibButtonClick(Sender: TObject); diff --git a/Src/AST/Myc.Fmx.AstEditor.Controller.pas b/Src/AST/Myc.Fmx.AstEditor.Controller.pas deleted file mode 100644 index 7a9a4db..0000000 --- a/Src/AST/Myc.Fmx.AstEditor.Controller.pas +++ /dev/null @@ -1,432 +0,0 @@ -unit Myc.Fmx.AstEditor.Controller; - -interface - -uses - System.SysUtils, - System.Classes, - System.Types, - System.Math.Vectors, - System.Generics.Collections, - FMX.Types, - FMX.Controls, - Myc.Ast, - Myc.Ast.Nodes, - Myc.Ast.Identities, - Myc.Ast.Types, - Myc.Ast.Visitor, - Myc.Ast.Scope, - Myc.Ast.Environment, - Myc.Fmx.AstEditor, - Myc.Fmx.AstEditor.Workspace; - -type - TAstEditorController = class - private - type - TNodeMap = TDictionary; - TUndoStack = TStack; - - TTypePropagator = class(TAstVisitor) - private - FMap: TNodeMap; - procedure ApplyType(const Node: IAstNode); - protected - procedure Accept(const Node: IAstNode); override; - public - constructor Create(AMap: TNodeMap); - end; - - private - FEnv: TAstEnvironment; - FWorkspace: TWorkspace; - FNodeMap: TNodeMap; - FRootViewNode: TAstViewNode; - - // State Tracking - FCurrentAst: IAstNode; - - // Undo/Redo Stacks - FUndoStack: TUndoStack; - FRedoStack: TUndoStack; - FIsUndoing: Boolean; - - procedure CollectNodes(Parent: TControl); - procedure ClearVisuals; - procedure ApplyErrors(const Log: ICompilerLog); - procedure ApplyTypes(const TypedAst: IAstNode); - procedure RootResized(Sender: TObject); - - procedure HandleNodeDragDrop(ASource, ATarget: TControl; AIndex: Integer); - - procedure CommitSnapshot; - - public - constructor Create(const AEnv: TAstEnvironment; AWorkspace: TWorkspace); - destructor Destroy; override; - - procedure ValidateAndShow; - procedure SetRoot(const AstNode: IAstNode); - - procedure Undo; - procedure Redo; - function CanUndo: Boolean; - function CanRedo: Boolean; - - // Expose current AST for external use (saving etc.) - property CurrentAst: IAstNode read FCurrentAst; - end; - -implementation - -uses - Myc.Data.Value, - Myc.Fmx.AstEditor.Visualizer; - -{ TAstEditorController } - -constructor TAstEditorController.Create(const AEnv: TAstEnvironment; AWorkspace: TWorkspace); -begin - inherited Create; - FEnv := AEnv; - FWorkspace := AWorkspace; - FWorkspace.OnNodeDragDrop := HandleNodeDragDrop; - FNodeMap := TNodeMap.Create; - - FUndoStack := TUndoStack.Create; - FRedoStack := TUndoStack.Create; - FIsUndoing := False; - FCurrentAst := nil; -end; - -destructor TAstEditorController.Destroy; -begin - // Cleaning up FCurrentAst is done by ARC automatically - SetRoot(nil); - FNodeMap.Free; - FUndoStack.Free; - FRedoStack.Free; - inherited; -end; - -procedure TAstEditorController.CommitSnapshot; -begin - // Optimization: Push the already known AST instead of recreating it from UI. - // Since AST nodes are immutable interfaces, storing the reference is safe. - if Assigned(FCurrentAst) and (not FIsUndoing) then - begin - FUndoStack.Push(FCurrentAst); - FRedoStack.Clear; - end; -end; - -procedure TAstEditorController.Undo; -var - prevAst: IAstNode; -begin - if FUndoStack.Count = 0 then - exit; - - FIsUndoing := True; - try - // Capture current state for Redo - if Assigned(FCurrentAst) then - FRedoStack.Push(FCurrentAst); - - // Restore previous state - prevAst := FUndoStack.Pop; - SetRoot(prevAst); - finally - FIsUndoing := False; - end; -end; - -procedure TAstEditorController.Redo; -var - nextAst: IAstNode; -begin - if FRedoStack.Count = 0 then - exit; - - FIsUndoing := True; - try - // Capture current state for Undo - if Assigned(FCurrentAst) then - FUndoStack.Push(FCurrentAst); - - // Restore next state - nextAst := FRedoStack.Pop; - SetRoot(nextAst); - finally - FIsUndoing := False; - end; -end; - -function TAstEditorController.CanUndo: Boolean; -begin - Result := FUndoStack.Count > 0; -end; - -function TAstEditorController.CanRedo: Boolean; -begin - Result := FRedoStack.Count > 0; -end; - -procedure TAstEditorController.HandleNodeDragDrop(ASource, ATarget: TControl; AIndex: Integer); -var - SourceParent: TControl; - ContainerOwner: TAstViewNode; - Reorderable: IReorderable; - SourceIdx, TargetIdx: Integer; - NewAst: IAstNode; -begin - if (ASource = nil) or (AIndex < 0) then - exit; - if not (ASource is TAstViewNode) then - exit; - - if not (ASource.Parent is TControl) then - exit; - SourceParent := TControl(ASource.Parent); - - ContainerOwner := nil; - - // Logic to find the Reorderable container (same as before) - if (SourceParent is TAstViewNode) then - begin - ContainerOwner := TAstViewNode(SourceParent); - if not Supports(ContainerOwner.Handler, IReorderable, Reorderable) then - ContainerOwner := nil; - end; - - if (ContainerOwner = nil) and (SourceParent.Parent is TAstViewNode) then - begin - ContainerOwner := TAstViewNode(SourceParent.Parent); - if not Supports(ContainerOwner.Handler, IReorderable, Reorderable) then - ContainerOwner := nil; - end; - - if (ContainerOwner <> nil) and (Reorderable <> nil) then - begin - if Reorderable.CanDrag(ASource) then - begin - SourceIdx := ASource.Index; - TargetIdx := AIndex; - - if SourceIdx <> TargetIdx then - begin - // 1. Commit the CURRENT (clean) AST before visual changes - CommitSnapshot; - - // 2. Perform Visual Change (Dirty State) - Reorderable.MoveChild(SourceIdx, TargetIdx); - - // 3. Generate NEW AST from the modified visual tree - NewAst := FRootViewNode.CreateAst; - - // 4. Update the system with the new AST (Updates FCurrentAst) - SetRoot(NewAst); - end; - end; - end; -end; - -procedure TAstEditorController.SetRoot(const AstNode: IAstNode); -var - Visualizer: IAstVisualizer; -begin - // Update the "Truth" - FCurrentAst := AstNode; - - if not FIsUndoing then - begin - FUndoStack.Clear; - FRedoStack.Clear; - end; - - FWorkspace.World.DeleteChildren; - if Assigned(FRootViewNode) then - FRootViewNode.OnResized := nil; - FRootViewNode := nil; - - if not Assigned(AstNode) then - exit; - - Visualizer := TAstVisualizer.Create(FWorkspace, FWorkspace.World, 0); - FRootViewNode := Visualizer.CallAccept(AstNode); - - if Assigned(FRootViewNode) then - begin - FWorkspace.World.Size := FRootViewNode.Size; - FRootViewNode.OnResized := RootResized; - FRootViewNode.Position.Point := TPointF.Create(50, 50); - end; - - if Assigned(FRootViewNode) then - ValidateAndShow; -end; - -// ... (Rest: CollectNodes, ClearVisuals, ApplyErrors, ApplyTypes, RootResized, TTypePropagator bleiben gleich) - -procedure TAstEditorController.CollectNodes(Parent: TControl); -var - i: Integer; - child: TControl; - viewNode: TAstViewNode; -begin - for i := 0 to Parent.ChildrenCount - 1 do - begin - if Parent.Children[i] is TControl then - begin - child := TControl(Parent.Children[i]); - - if child is TAstViewNode then - begin - viewNode := TAstViewNode(child); - if Assigned(viewNode.Node) and Assigned(viewNode.Node.Identity) then - begin - FNodeMap.AddOrSetValue(viewNode.Node.Identity, viewNode); - end; - end; - - if child.ChildrenCount > 0 then - CollectNodes(child); - end; - end; -end; - -procedure TAstEditorController.ClearVisuals; -begin - for var viewNode in FNodeMap.Values do - begin - viewNode.ErrorMessage := ''; - viewNode.TypeInfoText := ''; - end; -end; - -procedure TAstEditorController.ApplyErrors(const Log: ICompilerLog); -var - entry: TCompilerError; - viewNode: TAstViewNode; -begin - for entry in Log.GetEntries do - begin - if (entry.Level = elError) and Assigned(entry.Node) and Assigned(entry.Node.Identity) then - begin - if FNodeMap.TryGetValue(entry.Node.Identity, viewNode) then - begin - if viewNode.ErrorMessage = '' then - viewNode.ErrorMessage := entry.Message - else - viewNode.ErrorMessage := viewNode.ErrorMessage + sLineBreak + entry.Message; - end; - end; - end; -end; - -procedure TAstEditorController.ApplyTypes(const TypedAst: IAstNode); -var - propagator: TTypePropagator; -begin - if not Assigned(TypedAst) then - exit; - - propagator := TTypePropagator.Create(FNodeMap); - try - propagator.Accept(TypedAst); - finally - propagator.Free; - end; -end; - -procedure TAstEditorController.RootResized(Sender: TObject); -begin - FWorkspace.World.Size := FRootViewNode.Size; -end; - -procedure TAstEditorController.ValidateAndShow; -var - logicalAst: IAstNode; -begin - if not Assigned(FRootViewNode) then - exit; - - // Note: We use FCurrentAst here if we trust it, or recreate for safety. - // Since SetRoot sets FCurrentAst, and any UI manipulation (DragDrop) calls SetRoot afterwards, - // FCurrentAst should be in sync. - // However, strictly speaking, ValidateAndShow operates on the VISUAL tree to find Mapping. - // But since we have FCurrentAst, we can use it directly for binding. - - // BUT: ValidateAndShow is responsible for mapping NodeMap. - logicalAst := FRootViewNode.CreateAst; - // ^ This is still needed to rebuild the Mapping if we haven't tracked ViewNodes perfectly. - // Or we rely on FCurrentAst being the source of truth and we assume ViewNodes match. - // For now, let's keep CreateAst here to be 100% sure the mapping corresponds to what is on screen. - // The performance hit here is acceptable as it's part of the validation cycle, not the drag-start. - - FNodeMap.Clear; - CollectNodes(FWorkspace.World); - - ClearVisuals; - - try - var log := TCompilerLog.Create as ICompilerLog; - - var expanded := FEnv.ExpandMacros(logicalAst); - var typedAst := FEnv.Bind(expanded, [], log); - var specAst := FEnv.Specialize(typedAst); - - ApplyTypes(specAst); - - if specAst.Kind = akLambdaExpression then - var compiled := FEnv.Link(specAst.AsLambdaExpression, log); - - ApplyErrors(log); - except - on E: ECompilationFailed do - begin - var tempLog := TCompilerLog.Create as ICompilerLog; - for var err in E.Errors do - tempLog.Add(err.Level, err.Message, err.Node); - ApplyErrors(tempLog); - end; - end; - - FWorkspace.World.Repaint; -end; - -{ TAstEditorController.TTypePropagator } - -constructor TAstEditorController.TTypePropagator.Create(AMap: TNodeMap); -begin - inherited Create; - FMap := AMap; -end; - -procedure TAstEditorController.TTypePropagator.ApplyType(const Node: IAstNode); -var - viewNode: TAstViewNode; -begin - if Assigned(Node) and Assigned(Node.Identity) then - begin - if FMap.TryGetValue(Node.Identity, viewNode) then - begin - if Node.IsTyped then - begin - var st := Node.AsTypedNode.StaticType; - if Assigned(st) then - viewNode.TypeInfoText := st.ToString; - end; - end; - end; -end; - -procedure TAstEditorController.TTypePropagator.Accept(const Node: IAstNode); -begin - if Node = nil then - exit; - ApplyType(Node); - Node.Accept(Self); -end; - -end. diff --git a/Src/AST/Myc.Fmx.AstEditor.Handlers.pas b/Src/AST/Myc.Fmx.AstEditor.Handlers.pas index edfcafb..270374e 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Handlers.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Handlers.pas @@ -18,7 +18,7 @@ uses Myc.Ast.Nodes, Myc.Ast.Identities, Myc.Fmx.AstEditor.Layout, - Myc.Fmx.AstEditor; + Myc.Fmx.AstEditor.Node; type // --- Abstract List Base --- diff --git a/Src/AST/Myc.Fmx.AstEditor.Node.pas b/Src/AST/Myc.Fmx.AstEditor.Node.pas new file mode 100644 index 0000000..5306f91 --- /dev/null +++ b/Src/AST/Myc.Fmx.AstEditor.Node.pas @@ -0,0 +1,542 @@ +unit Myc.Fmx.AstEditor.Node; + +interface + +uses + System.SysUtils, + System.Classes, + System.UITypes, + System.Types, + System.Math, + System.Math.Vectors, + System.Generics.Defaults, + System.Generics.Collections, + FMX.Types, + FMX.Controls, + FMX.Objects, + FMX.Graphics, + FMX.StdCtrls, + Myc.Ast.Nodes, + Myc.Fmx.AstEditor.Workspace, + Myc.Fmx.AstEditor.Layout; + +const + cNodePadding = 10; + cTitleTopPadding = 2; + cTitleBottomPadding = 4; + cMinNodeWidth = 10; + cMinNodeHeight = 10; + cDragGutterWidth = 20; // Width of the trigger zone for dragging + +type + TAstViewNode = class; + + IAstViewNodeHandler = interface + function GetAstNode: IAstNode; + procedure BuildUI(OwnerNode: TAstViewNode); + function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; + property Node: IAstNode read GetAstNode; + end; + + IReorderable = interface + ['{9FA7C504-D327-4E00-A5FE-DD2876886212}'] + function CanDrag(Child: TControl): Boolean; + procedure MoveChild(SourceIndex, TargetIndex: Integer); + end; + + IAstVisualizer = interface + {$region 'private'} + function GetExprDepth: Integer; + function GetParentControl: TControl; + function GetWorkspace: TWorkspace; + {$endregion} + + function Clone(ParentControl: TControl; ExprDepth: Integer): IAstVisualizer; + function CallAccept(const Node: IAstNode): TAstViewNode; + + property ExprDepth: Integer read GetExprDepth; + property ParentControl: TControl read GetParentControl; + property Workspace: TWorkspace read GetWorkspace; + end; + + TAstViewNode = class(TAutoFitControl) + private + FBackgroundColor: TAlphaColor; + FBorderColor: TAlphaColor; + FBorderWidth: Single; + FBorderRadius: Single; + FFrameless: Boolean; + FVisualizer: IAstVisualizer; + FHandler: IAstViewNodeHandler; + + FErrorMessage: string; + FTypeInfoText: string; + + FIsFocused: Boolean; + + procedure SetBackgroundColor(const Value: TAlphaColor); + procedure SetBorderColor(const Value: TAlphaColor); + procedure SetBorderWidth(const Value: Single); + procedure SetBorderRadius(const Value: Single); + procedure SetFrameless(const Value: Boolean); + function GetNode: IAstNode; + + procedure SetErrorMessage(const Value: string); + procedure SetTypeInfoText(const Value: string); + procedure UpdateVisualState; + procedure SetIsFocused(const Value: Boolean); + + protected + procedure Paint; override; + procedure SetupNode; + + procedure DoMouseEnter; override; + procedure DoMouseLeave; override; + procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override; + procedure MouseMove(Shift: TShiftState; X, Y: Single); override; + procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override; + + public + constructor Create(const AVisualizer: IAstVisualizer; const AHandler: IAstViewNodeHandler); reintroduce; + destructor Destroy; override; + + procedure AfterConstruction; override; + + function AddLabel(Parent: TControl; const Txt: String): TLabel; + function AddContainer(Parent: TControl; Orientation: TLayoutOrientation; Alignment: TLayoutAlignment): TAutoFitControl; + function AddExpr(Parent: TControl; const Title: String; const Node: IAstNode): TAstViewNode; + + function CreateAst: IAstNode; + + property Visualizer: IAstVisualizer read FVisualizer; + property Node: IAstNode read GetNode; + property Handler: IAstViewNodeHandler read FHandler; + + property ErrorMessage: string read FErrorMessage write SetErrorMessage; + property TypeInfoText: string read FTypeInfoText write SetTypeInfoText; + property IsFocused: Boolean read FIsFocused write SetIsFocused; + + published + property BorderColor: TAlphaColor read FBorderColor write SetBorderColor; + property BorderWidth: Single read FBorderWidth write SetBorderWidth; + property BorderRadius: Single read FBorderRadius write SetBorderRadius; + property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor; + property Frameless: Boolean read FFrameless write SetFrameless; + + property Align; + property Anchors; + property ClipChildren default True; + property Cursor default crDefault; + property DragMode default TDragMode.dmManual; + property Enabled; + property Height; + property Hint; + property HitTest default True; + property Locked; + property Margins; + property Opacity; + property Padding; + property PopupMenu; + property Position; + property RotationAngle; + property RotationCenter; + property Scale; + property Size; + property Visible; + property Width; + property OnClick; + property OnDblClick; + property OnMouseDown; + property OnMouseMove; + property OnMouseUp; + property OnMouseWheel; + property OnMouseEnter; + property OnMouseLeave; + end; + + TBaseNodeHandler = class(TInterfacedObject, IAstViewNodeHandler) + protected + FNode: T; + function GetAstNode: IAstNode; + public + constructor Create(const ANode: T); + procedure BuildUI(OwnerNode: TAstViewNode); virtual; abstract; + function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; virtual; abstract; + end; + +implementation + +uses + Myc.Ast; + +{ TAstViewNode } + +constructor TAstViewNode.Create(const AVisualizer: IAstVisualizer; const AHandler: IAstViewNodeHandler); +begin + inherited Create(AVisualizer.Workspace); + + Parent := AVisualizer.ParentControl; + + FVisualizer := AVisualizer.Clone(Self, AVisualizer.ExprDepth); + FHandler := AHandler; + + FBorderColor := TAlphaColors.Gray; + FBorderWidth := 1; + FBorderRadius := 9; + FBackgroundColor := $080a0a0a; + + FFrameless := False; + FIsFocused := False; + + Width := cMinNodeWidth; + Height := cMinNodeHeight; + + HitTest := True; + ClipChildren := True; + + Margins.Left := 4; + Margins.Top := 4; + Margins.Right := 4; + Margins.Bottom := 4; + Padding.Left := 3; + Padding.Top := 3; + Padding.Right := 3; + Padding.Bottom := 3; + + var cn: Cardinal := $ea - (7 * FVisualizer.ExprDepth); + var c: Cardinal := $ff000000 or (cn shl 16) or (cn shl 8) or cn; + BackgroundColor := c; +end; + +destructor TAstViewNode.Destroy; +begin + FHandler := nil; + inherited; +end; + +procedure TAstViewNode.DoMouseEnter; +begin + inherited; + Repaint; +end; + +procedure TAstViewNode.DoMouseLeave; +begin + inherited; + Repaint; +end; + +procedure TAstViewNode.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); +const + cHeaderHeight = 30; // Defined height for header drag area +begin + // Check for Drag Start. + // Condition: Left Click AND we are part of a container (list) + if (Button = TMouseButton.mbLeft) and (Parent is TAutoFitControl) then + begin + // Hit-Test: Either Gutter (Left) OR Header area (Top) + if (X < cDragGutterWidth) or (Y < cHeaderHeight) then + begin + if Assigned(FVisualizer.Workspace) then + begin + FVisualizer.Workspace.BeginDragNode(Self); + Exit; + end; + end; + end; + + inherited; +end; + +procedure TAstViewNode.MouseMove(Shift: TShiftState; X, Y: Single); +const + cHeaderHeight = 30; +begin + // Cursor Logic (Hover Feedback) + if (Parent is TAutoFitControl) and ((X < cDragGutterWidth) or (Y < cHeaderHeight)) then + Cursor := crSizeAll + else + Cursor := crDefault; + + inherited; +end; + +procedure TAstViewNode.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); +begin + inherited; +end; + +function TAstViewNode.AddContainer(Parent: TControl; Orientation: TLayoutOrientation; Alignment: TLayoutAlignment): TAutoFitControl; +begin + Result := TAutoFitControl.Create(Self); + Result.Parent := Parent; + Result.Orientation := Orientation; + Result.Alignment := Alignment; + Result.HitTest := False; +end; + +function TAstViewNode.AddExpr(Parent: TControl; const Title: String; const Node: IAstNode): TAstViewNode; +begin + var cont := AddContainer(Parent, loHorizontal, laCenter); + + var lbl := AddLabel(cont, Title); + lbl.Font.Style := lbl.Font.Style + [TFontStyle.fsBold]; + + Result := FVisualizer.Clone(cont, FVisualizer.ExprDepth + 1).CallAccept(Node); +end; + +function TAstViewNode.AddLabel(Parent: TControl; const Txt: String): TLabel; +begin + Result := TLabel.Create(Self); + Result.Parent := Parent; + + Result.Position.Point := TPoint.Create(cNodePadding, cNodePadding); + Result.Margins.Left := cNodePadding; + Result.Margins.Right := cNodePadding; + Result.Margins.Top := cTitleTopPadding; + Result.Margins.Bottom := cTitleBottomPadding; + Result.WordWrap := False; + Result.AutoSize := True; + Result.HitTest := False; + + Result.StyledSettings := Result.StyledSettings - [TStyledSetting.Style]; + + Result.Text := Txt; + Result.ApplyStyleLookup; +end; + +procedure TAstViewNode.AfterConstruction; +begin + inherited; + SetupNode; +end; + +function TAstViewNode.CreateAst: IAstNode; +begin + if Assigned(FHandler) then + Result := FHandler.ReconstructAst(Self) + else + Result := TAst.Nop; +end; + +function TAstViewNode.GetNode: IAstNode; +begin + if Assigned(FHandler) then + Result := FHandler.Node + else + Result := nil; +end; + +procedure TAstViewNode.SetErrorMessage(const Value: string); +begin + if FErrorMessage <> Value then + begin + FErrorMessage := Value; + UpdateVisualState; + Repaint; + end; +end; + +procedure TAstViewNode.SetTypeInfoText(const Value: string); +begin + if FTypeInfoText <> Value then + begin + FTypeInfoText := Value; + UpdateVisualState; + end; +end; + +procedure TAstViewNode.SetIsFocused(const Value: Boolean); +begin + if FIsFocused <> Value then + begin + FIsFocused := Value; + Repaint; + end; +end; + +procedure TAstViewNode.UpdateVisualState; +begin + if FErrorMessage <> '' then + begin + Hint := 'Error: ' + FErrorMessage; + ShowHint := True; + end + else if FTypeInfoText <> '' then + begin + Hint := 'Type: ' + FTypeInfoText; + ShowHint := True; + end + else + begin + Hint := ''; + ShowHint := False; + end; +end; + +procedure TAstViewNode.Paint; +var + rect: TRectF; + effBorderColor: TAlphaColor; + effBorderWidth: Single; + effDash: TStrokeDash; + isHovering: Boolean; + shouldIgnoreHover: Boolean; +begin + inherited; + + shouldIgnoreHover := False; + if Assigned(Node) then + begin + case Node.Kind of + akBlockExpression, akParameterList, akArgumentList, akExpressionList, akRecordFieldList: shouldIgnoreHover := True; + end; + end; + + isHovering := IsMouseOver and (not shouldIgnoreHover); + + // 1. Determine Base Style + if FFrameless then + begin + effBorderColor := FBorderColor; + effBorderWidth := FBorderWidth; + effDash := TStrokeDash.Dot; + end + else + begin + effBorderColor := FBorderColor; + effBorderWidth := FBorderWidth; + effDash := TStrokeDash.Solid; + end; + + // 2. Hover Overlay + if isHovering then + begin + effBorderColor := TAlphaColors.White; + effBorderWidth := Max(2.0, FBorderWidth + 1.0); + effDash := TStrokeDash.Solid; + end; + + // 3. Error Overlay (High Priority for Color) + if FErrorMessage <> '' then + begin + effBorderColor := TAlphaColors.Red; + effBorderWidth := 2.0; + effDash := TStrokeDash.Solid; + end; + + // 4. Focus Overlay (Highest Priority for Visibility/Color) + // Note: If Error is present, Focus will override color to Blue to indicate "Active Cursor". + // Error text is available in Hint. + if FIsFocused then + begin + effBorderColor := TAlphaColors.Dodgerblue; + effBorderWidth := 2.0; + effDash := TStrokeDash.Solid; + end; + + Canvas.Fill.Kind := TBrushKind.Solid; + Canvas.Fill.Color := FBackgroundColor; + + if FFrameless and (FErrorMessage = '') and (not isHovering) and (not FIsFocused) then + begin + Canvas.FillRect(TRectF.Create(0, 0, Width, Height), 0, 0, [], 1.0); + + if FBorderWidth > 0 then + begin + Canvas.Stroke.Kind := TBrushKind.Solid; + Canvas.Stroke.Color := effBorderColor; + Canvas.Stroke.Thickness := effBorderWidth; + Canvas.Stroke.Dash := effDash; + Canvas.DrawRect(TRectF.Create(0, 0, Width, Height), 0, 0, [], 1.0); + end; + end + else + begin + rect := TRectF.Create(0, 0, Width, Height); + if (effBorderWidth > 0) then + rect.Inflate(-effBorderWidth / 2, -effBorderWidth / 2); + + Canvas.FillRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round); + + if (effBorderWidth > 0) and (effBorderColor <> TAlphaColors.Null) then + begin + Canvas.Stroke.Kind := TBrushKind.Solid; + Canvas.Stroke.Color := effBorderColor; + Canvas.Stroke.Thickness := effBorderWidth; + Canvas.Stroke.Dash := effDash; + Canvas.DrawRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round); + end; + end; + + if isHovering and (Parent is TAutoFitControl) then + begin + Canvas.Fill.Color := TAlphaColors.DimGray; + Canvas.FillEllipse(TRectF.Create(4, 6, 8, 10), 1.0); + Canvas.FillEllipse(TRectF.Create(4, 12, 8, 16), 1.0); + Canvas.FillEllipse(TRectF.Create(4, 18, 8, 22), 1.0); + end; +end; + +procedure TAstViewNode.SetBorderColor(const Value: TAlphaColor); +begin + if FBorderColor <> Value then + begin + FBorderColor := Value; + Repaint; + end; +end; + +procedure TAstViewNode.SetBorderRadius(const Value: Single); +begin + if FBorderRadius <> Value then + begin + FBorderRadius := Value; + Repaint; + end; +end; + +procedure TAstViewNode.SetBorderWidth(const Value: Single); +begin + if FBorderWidth <> Value then + begin + FBorderWidth := Value; + Repaint; + end; +end; + +procedure TAstViewNode.SetFrameless(const Value: Boolean); +begin + if FFrameless <> Value then + begin + FFrameless := Value; + Repaint; + end; +end; + +procedure TAstViewNode.SetBackgroundColor(const Value: TAlphaColor); +begin + FBackgroundColor := Value; + Repaint; +end; + +procedure TAstViewNode.SetupNode; +begin + if Assigned(FHandler) then + FHandler.BuildUI(Self); +end; + +{ TBaseNodeHandler } + +constructor TBaseNodeHandler.Create(const ANode: T); +begin + inherited Create; + FNode := ANode; +end; + +function TBaseNodeHandler.GetAstNode: IAstNode; +begin + Result := FNode; +end; + +end. diff --git a/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas b/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas index 6f3388c..fae9a98 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas @@ -10,7 +10,7 @@ uses Myc.Ast.Nodes, Myc.Ast.Visitor, Myc.Fmx.AstEditor.Workspace, - Myc.Fmx.AstEditor, + Myc.Fmx.AstEditor.Node, Myc.Fmx.AstEditor.Handlers; type diff --git a/Src/AST/Myc.Fmx.AstEditor.Workspace.pas b/Src/AST/Myc.Fmx.AstEditor.Workspace.pas index d9b20a4..d8c1595 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Workspace.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Workspace.pas @@ -18,7 +18,6 @@ uses type TOnPaintConnectionsEvent = procedure(ASender: TObject; ACanvas: TCanvas) of object; - TOnDragDropEvent = procedure(ASource, ATarget: TControl; AIndex: Integer) of object; TAstWorld = class(TControl) @@ -29,7 +28,7 @@ type FDropP1, FDropP2: TPointF; protected procedure Paint; override; - procedure AfterPaint; override; // <--- HIER: Für Zeichnen ÜBER den Kindern + procedure AfterPaint; override; public constructor Create(AOwner: TComponent); override; procedure InvalidateConnections; @@ -163,6 +162,8 @@ begin inherited Create(AOwner); ClipChildren := True; HitTest := True; + CanFocus := True; // Enable Keyboard Input + AutoCapture := True; FRootLayer := TAstWorld.Create(Self); FRootLayer.Parent := Self; @@ -369,6 +370,10 @@ end; procedure TWorkspace.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); begin inherited; + // Capture Keyboard Focus + if CanFocus then + SetFocus; + if not FIsDraggingNode and (Button in [TMouseButton.mbLeft, TMouseButton.mbMiddle]) then begin FIsPanning := True; diff --git a/Src/AST/Myc.Fmx.AstEditor.pas b/Src/AST/Myc.Fmx.AstEditor.pas index 13fb963..d29bacd 100644 --- a/Src/AST/Myc.Fmx.AstEditor.pas +++ b/Src/AST/Myc.Fmx.AstEditor.pas @@ -5,511 +5,1007 @@ interface uses System.SysUtils, System.Classes, - System.UITypes, System.Types, + System.UITypes, System.Math, System.Math.Vectors, - System.Generics.Defaults, System.Generics.Collections, FMX.Types, FMX.Controls, - FMX.Objects, FMX.Graphics, - FMX.StdCtrls, + Myc.Ast, Myc.Ast.Nodes, - Myc.Fmx.AstEditor.Workspace, - Myc.Fmx.AstEditor.Layout; - -const - cNodePadding = 10; - cTitleTopPadding = 2; - cTitleBottomPadding = 4; - cMinNodeWidth = 10; - cMinNodeHeight = 10; - cDragGutterWidth = 20; // Width of the trigger zone for dragging + Myc.Ast.Identities, + Myc.Ast.Types, + Myc.Ast.Visitor, + Myc.Ast.Scope, + Myc.Ast.Environment, + Myc.Fmx.AstEditor.Node, + Myc.Fmx.AstEditor.Layout, + Myc.Fmx.AstEditor.Workspace; type - TAstViewNode = class; - - IAstViewNodeHandler = interface - function GetAstNode: IAstNode; - procedure BuildUI(OwnerNode: TAstViewNode); - function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; - property Node: IAstNode read GetAstNode; - end; - - IReorderable = interface - ['{9FA7C504-D327-4E00-A5FE-DD2876886212}'] - function CanDrag(Child: TControl): Boolean; - procedure MoveChild(SourceIndex, TargetIndex: Integer); - end; - - IAstVisualizer = interface - {$region 'private'} - function GetExprDepth: Integer; - function GetParentControl: TControl; - function GetWorkspace: TWorkspace; - {$endregion} - - function Clone(ParentControl: TControl; ExprDepth: Integer): IAstVisualizer; - function CallAccept(const Node: IAstNode): TAstViewNode; - - property ExprDepth: Integer read GetExprDepth; - property ParentControl: TControl read GetParentControl; - property Workspace: TWorkspace read GetWorkspace; - end; - - TAstViewNode = class(TAutoFitControl) + TAstEditor = class(TControl) private - FBackgroundColor: TAlphaColor; - FBorderColor: TAlphaColor; - FBorderWidth: Single; - FBorderRadius: Single; - FFrameless: Boolean; - FVisualizer: IAstVisualizer; - FHandler: IAstViewNodeHandler; + type + TNodeMap = TDictionary; + TUndoStack = TStack; - FErrorMessage: string; - FTypeInfoText: string; + TTypePropagator = class(TAstVisitor) + private + FMap: TNodeMap; + procedure ApplyType(const Node: IAstNode); + protected + procedure Accept(const Node: IAstNode); override; + public + constructor Create(AMap: TNodeMap); + end; - procedure SetBackgroundColor(const Value: TAlphaColor); - procedure SetBorderColor(const Value: TAlphaColor); - procedure SetBorderWidth(const Value: Single); - procedure SetBorderRadius(const Value: Single); - procedure SetFrameless(const Value: Boolean); - function GetNode: IAstNode; + private + FEnv: TAstEnvironment; + FWorkspace: TWorkspace; + FNodeMap: TNodeMap; + FRootViewNode: TAstViewNode; + FFocusedNode: TAstViewNode; - procedure SetErrorMessage(const Value: string); - procedure SetTypeInfoText(const Value: string); - procedure UpdateVisualState; + // State Tracking + FCurrentAst: IAstNode; + + // Undo/Redo Stacks + FUndoStack: TUndoStack; + FRedoStack: TUndoStack; + FIsUndoing: Boolean; + + // Events + FOnPaintConnections: TOnPaintConnectionsEvent; + FOnWorkspaceMouseDown: TMouseEvent; + + procedure CollectNodes(Parent: TControl); + procedure ClearVisuals; + procedure ApplyErrors(const Log: ICompilerLog); + procedure ApplyTypes(const TypedAst: IAstNode); + procedure RootResized(Sender: TObject); + + // Internal Event Wiring + procedure HandleNodeDragDrop(ASource, ATarget: TControl; AIndex: Integer); + procedure HandleKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); + procedure HandleWorkspacePaintConnections(ASender: TObject; ACanvas: TCanvas); + procedure HandleWorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); + + // NEU: Handler für Klicks auf die Nodes selbst + procedure HandleNodeMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); + + procedure CommitSnapshot; + + // --- Navigation Helper --- + procedure EnsureVisible(Node: TAstViewNode); + function GetParentNode(StartNode: TAstViewNode): TAstViewNode; + function GetFirstChild(StartNode: TAstViewNode): TAstViewNode; + function GetNextVerticalNeighbor(StartNode: TAstViewNode): TAstViewNode; + function GetPrevVerticalNeighbor(StartNode: TAstViewNode): TAstViewNode; + function GetNextLinearNode(StartNode: TAstViewNode): TAstViewNode; + function GetPrevLinearNode(StartNode: TAstViewNode): TAstViewNode; + + procedure SetOnPaintConnections(const Value: TOnPaintConnectionsEvent); protected - procedure Paint; override; - procedure SetupNode; - - procedure DoMouseEnter; override; - procedure DoMouseLeave; override; - procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override; - procedure MouseMove(Shift: TShiftState; X, Y: Single); override; - procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override; + procedure Paint; override; // For design-time border public - constructor Create(const AVisualizer: IAstVisualizer; const AHandler: IAstViewNodeHandler); reintroduce; + constructor Create(AOwner: TComponent); override; destructor Destroy; override; - procedure AfterConstruction; override; + // Must be called after creation to link the AST Environment + procedure Init(const AEnv: TAstEnvironment); - function AddLabel(Parent: TControl; const Txt: String): TLabel; - function AddContainer(Parent: TControl; Orientation: TLayoutOrientation; Alignment: TLayoutAlignment): TAutoFitControl; - function AddExpr(Parent: TControl; const Title: String; const Node: IAstNode): TAstViewNode; + procedure ValidateAndShow; + procedure SetRoot(const AstNode: IAstNode); - function CreateAst: IAstNode; + procedure Undo; + procedure Redo; + function CanUndo: Boolean; + function CanRedo: Boolean; - property Visualizer: IAstVisualizer read FVisualizer; - property Node: IAstNode read GetNode; - property Handler: IAstViewNodeHandler read FHandler; + procedure SetFocusedNode(Node: TAstViewNode); + procedure Navigate(Key: Word; Shift: TShiftState); - property ErrorMessage: string read FErrorMessage write SetErrorMessage; - property TypeInfoText: string read FTypeInfoText write SetTypeInfoText; + property CurrentAst: IAstNode read FCurrentAst; + property FocusedNode: TAstViewNode read FFocusedNode; published - property BorderColor: TAlphaColor read FBorderColor write SetBorderColor; - property BorderWidth: Single read FBorderWidth write SetBorderWidth; - property BorderRadius: Single read FBorderRadius write SetBorderRadius; - property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor; - property Frameless: Boolean read FFrameless write SetFrameless; - property Align; property Anchors; - property ClipChildren default True; - property Cursor default crDefault; - property DragMode default TDragMode.dmManual; - property Enabled; - property Height; - property Hint; - property HitTest default True; - property Locked; - property Margins; - property Opacity; - property Padding; - property PopupMenu; property Position; - property RotationAngle; - property RotationCenter; - property Scale; - property Size; - property Visible; property Width; - property OnClick; - property OnDblClick; - property OnMouseDown; - property OnMouseMove; - property OnMouseUp; - property OnMouseWheel; - property OnMouseEnter; - property OnMouseLeave; - end; + property Height; + property Visible; + property Enabled; + property Opacity; - TBaseNodeHandler = class(TInterfacedObject, IAstViewNodeHandler) - protected - FNode: T; - function GetAstNode: IAstNode; - public - constructor Create(const ANode: T); - procedure BuildUI(OwnerNode: TAstViewNode); virtual; abstract; - function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; virtual; abstract; + // Expose events relevant to the outside world + property OnPaintConnections: TOnPaintConnectionsEvent read FOnPaintConnections write SetOnPaintConnections; + property OnMouseDown: TMouseEvent read FOnWorkspaceMouseDown write FOnWorkspaceMouseDown; end; implementation uses - Myc.Ast; + Myc.Data.Value, + Myc.Fmx.AstEditor.Visualizer; -{ TAstViewNode } +{ TAstEditor } -constructor TAstViewNode.Create(const AVisualizer: IAstVisualizer; const AHandler: IAstViewNodeHandler); +constructor TAstEditor.Create(AOwner: TComponent); begin - inherited Create(AVisualizer.Workspace); - - Parent := AVisualizer.ParentControl; - - FVisualizer := AVisualizer.Clone(Self, AVisualizer.ExprDepth); - FHandler := AHandler; - - FBorderColor := TAlphaColors.Gray; - FBorderWidth := 1; - FBorderRadius := 9; - FBackgroundColor := $080a0a0a; - - FFrameless := False; - - Width := cMinNodeWidth; - Height := cMinNodeHeight; - - HitTest := True; + inherited Create(AOwner); + Width := 400; + Height := 300; ClipChildren := True; - Margins.Left := 4; - Margins.Top := 4; - Margins.Right := 4; - Margins.Bottom := 4; - Padding.Left := 3; - Padding.Top := 3; - Padding.Right := 3; - Padding.Bottom := 3; + // Create Internal Workspace + FWorkspace := TWorkspace.Create(Self); + FWorkspace.Parent := Self; + FWorkspace.Align := TAlignLayout.Client; + FWorkspace.Stored := False; + FWorkspace.Locked := True; - var cn: Cardinal := $ea - (7 * FVisualizer.ExprDepth); - var c: Cardinal := $ff000000 or (cn shl 16) or (cn shl 8) or cn; - BackgroundColor := c; + // Wire Internal Events + FWorkspace.OnNodeDragDrop := HandleNodeDragDrop; + FWorkspace.OnKeyDown := HandleKeyDown; + FWorkspace.OnPaintConnections := HandleWorkspacePaintConnections; + FWorkspace.OnMouseDown := HandleWorkspaceMouseDown; + + FNodeMap := TNodeMap.Create; + FUndoStack := TUndoStack.Create; + FRedoStack := TUndoStack.Create; + FIsUndoing := False; + FCurrentAst := nil; end; -destructor TAstViewNode.Destroy; +destructor TAstEditor.Destroy; begin - FHandler := nil; + SetRoot(nil); + FNodeMap.Free; + FUndoStack.Free; + FRedoStack.Free; inherited; end; -procedure TAstViewNode.DoMouseEnter; +procedure TAstEditor.Init(const AEnv: TAstEnvironment); begin - inherited; - Repaint; + FEnv := AEnv; end; -procedure TAstViewNode.DoMouseLeave; +procedure TAstEditor.Paint; begin inherited; - Repaint; -end; - -procedure TAstViewNode.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); -const - cHeaderHeight = 30; // Defined height for header drag area -begin - // Check for Drag Start. - // Condition: Left Click AND we are part of a container (list) - if (Button = TMouseButton.mbLeft) and (Parent is TAutoFitControl) then + if (csDesigning in ComponentState) then begin - // Hit-Test: Either Gutter (Left) OR Header area (Top) - if (X < cDragGutterWidth) or (Y < cHeaderHeight) then + Canvas.Stroke.Kind := TBrushKind.Solid; + Canvas.Stroke.Color := TAlphaColors.Gray; + Canvas.Stroke.Dash := TStrokeDash.Dash; + Canvas.DrawRect(LocalRect, 0, 0, AllCorners, 1.0); + end; +end; + +procedure TAstEditor.SetOnPaintConnections(const Value: TOnPaintConnectionsEvent); +begin + FOnPaintConnections := Value; +end; + +procedure TAstEditor.HandleWorkspacePaintConnections(ASender: TObject; ACanvas: TCanvas); +begin + if Assigned(FOnPaintConnections) then + FOnPaintConnections(Self, ACanvas); +end; + +procedure TAstEditor.HandleWorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); +begin + // Wenn wir in den leeren Bereich klicken: + // 1. Tastaturfokus sicherstellen (wichtig!) + if FWorkspace.CanFocus then + FWorkspace.SetFocus; + + // Optional: Fokus löschen, wenn man ins Leere klickt? + // SetFocusedNode(nil); // Geschmackssache. Viele Editoren behalten den Fokus. + + if Assigned(FOnWorkspaceMouseDown) then + FOnWorkspaceMouseDown(Self, Button, Shift, X, Y); +end; + +procedure TAstEditor.HandleNodeMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); +begin + // 1. Fokus für Navigation sicherstellen (Keyboard Events gehen an Workspace) + if FWorkspace.CanFocus then + FWorkspace.SetFocus; + + // 2. Visuellen/Logischen Node-Fokus setzen + if (Button = TMouseButton.mbLeft) and (Sender is TAstViewNode) then + begin + SetFocusedNode(TAstViewNode(Sender)); + + // Stoppt die Event-Propagation nach oben zum Workspace, damit wir nicht + // sofort wieder deselektieren (falls wir das im WorkspaceMouseDown implementiert hätten). + // TControl.MouseDown hat kein 'Handled', aber wir sind hier im Event-Handler. + end; +end; + +procedure TAstEditor.CommitSnapshot; +begin + if Assigned(FCurrentAst) and (not FIsUndoing) then + begin + FUndoStack.Push(FCurrentAst); + FRedoStack.Clear; + end; +end; + +procedure TAstEditor.Undo; +var + prevAst: IAstNode; +begin + if FUndoStack.Count = 0 then + exit; + + FIsUndoing := True; + try + if Assigned(FCurrentAst) then + FRedoStack.Push(FCurrentAst); + + prevAst := FUndoStack.Pop; + SetRoot(prevAst); + finally + FIsUndoing := False; + end; +end; + +procedure TAstEditor.Redo; +var + nextAst: IAstNode; +begin + if FRedoStack.Count = 0 then + exit; + + FIsUndoing := True; + try + if Assigned(FCurrentAst) then + FUndoStack.Push(FCurrentAst); + + nextAst := FRedoStack.Pop; + SetRoot(nextAst); + finally + FIsUndoing := False; + end; +end; + +function TAstEditor.CanUndo: Boolean; +begin + Result := FUndoStack.Count > 0; +end; + +function TAstEditor.CanRedo: Boolean; +begin + Result := FRedoStack.Count > 0; +end; + +procedure TAstEditor.HandleNodeDragDrop(ASource, ATarget: TControl; AIndex: Integer); +var + SourceParent: TControl; + ContainerOwner: TAstViewNode; + Reorderable: IReorderable; + SourceIdx, TargetIdx: Integer; + NewAst: IAstNode; +begin + if (ASource = nil) or (AIndex < 0) then + exit; + if not (ASource is TAstViewNode) then + exit; + + if not (ASource.Parent is TControl) then + exit; + SourceParent := TControl(ASource.Parent); + + ContainerOwner := nil; + + if (SourceParent is TAstViewNode) then + begin + ContainerOwner := TAstViewNode(SourceParent); + if not Supports(ContainerOwner.Handler, IReorderable, Reorderable) then + ContainerOwner := nil; + end; + + if (ContainerOwner = nil) and (SourceParent.Parent is TAstViewNode) then + begin + ContainerOwner := TAstViewNode(SourceParent.Parent); + if not Supports(ContainerOwner.Handler, IReorderable, Reorderable) then + ContainerOwner := nil; + end; + + if (ContainerOwner <> nil) and (Reorderable <> nil) then + begin + if Reorderable.CanDrag(ASource) then begin - if Assigned(FVisualizer.Workspace) then + SourceIdx := ASource.Index; + TargetIdx := AIndex; + + if SourceIdx <> TargetIdx then begin - FVisualizer.Workspace.BeginDragNode(Self); - Exit; + CommitSnapshot; + Reorderable.MoveChild(SourceIdx, TargetIdx); + NewAst := FRootViewNode.CreateAst; + SetRoot(NewAst); + + // Optional: Fokus auf den verschobenen Node wiederherstellen + // Da der Node neu erzeugt wurde, müssten wir ihn über die Identity suchen. end; end; end; - - inherited; end; -procedure TAstViewNode.MouseMove(Shift: TShiftState; X, Y: Single); -const - cHeaderHeight = 30; +procedure TAstEditor.HandleKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); begin - // Cursor Logic (Hover Feedback) - if (Parent is TAutoFitControl) and ((X < cDragGutterWidth) or (Y < cHeaderHeight)) then - Cursor := crSizeAll - else - Cursor := crDefault; - - inherited; -end; - -procedure TAstViewNode.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); -begin - inherited; -end; - -function TAstViewNode.AddContainer(Parent: TControl; Orientation: TLayoutOrientation; Alignment: TLayoutAlignment): TAutoFitControl; -begin - Result := TAutoFitControl.Create(Self); - Result.Parent := Parent; - Result.Orientation := Orientation; - Result.Alignment := Alignment; - Result.HitTest := False; -end; - -function TAstViewNode.AddExpr(Parent: TControl; const Title: String; const Node: IAstNode): TAstViewNode; -begin - var cont := AddContainer(Parent, loHorizontal, laCenter); - - var lbl := AddLabel(cont, Title); - lbl.Font.Style := lbl.Font.Style + [TFontStyle.fsBold]; - - Result := FVisualizer.Clone(cont, FVisualizer.ExprDepth + 1).CallAccept(Node); -end; - -function TAstViewNode.AddLabel(Parent: TControl; const Txt: String): TLabel; -begin - Result := TLabel.Create(Self); - Result.Parent := Parent; - - Result.Position.Point := TPoint.Create(cNodePadding, cNodePadding); - Result.Margins.Left := cNodePadding; - Result.Margins.Right := cNodePadding; - Result.Margins.Top := cTitleTopPadding; - Result.Margins.Bottom := cTitleBottomPadding; - Result.WordWrap := False; - Result.AutoSize := True; - Result.HitTest := False; - - Result.StyledSettings := Result.StyledSettings - [TStyledSetting.Style]; - - Result.Text := Txt; - Result.ApplyStyleLookup; -end; - -procedure TAstViewNode.AfterConstruction; -begin - inherited; - SetupNode; -end; - -function TAstViewNode.CreateAst: IAstNode; -begin - if Assigned(FHandler) then - Result := FHandler.ReconstructAst(Self) - else - Result := TAst.Nop; -end; - -function TAstViewNode.GetNode: IAstNode; -begin - if Assigned(FHandler) then - Result := FHandler.Node - else - Result := nil; -end; - -procedure TAstViewNode.SetErrorMessage(const Value: string); -begin - if FErrorMessage <> Value then + if Key in [vkLeft, vkRight, vkUp, vkDown, vkTab, vkEscape] then begin - FErrorMessage := Value; - UpdateVisualState; - Repaint; + Navigate(Key, Shift); + Key := 0; end; end; -procedure TAstViewNode.SetTypeInfoText(const Value: string); -begin - if FTypeInfoText <> Value then - begin - FTypeInfoText := Value; - UpdateVisualState; - end; -end; - -procedure TAstViewNode.UpdateVisualState; -begin - if FErrorMessage <> '' then - begin - Hint := 'Error: ' + FErrorMessage; - ShowHint := True; - end - else if FTypeInfoText <> '' then - begin - Hint := 'Type: ' + FTypeInfoText; - ShowHint := True; - end - else - begin - Hint := ''; - ShowHint := False; - end; -end; - -procedure TAstViewNode.Paint; +procedure TAstEditor.SetRoot(const AstNode: IAstNode); var - rect: TRectF; - effBorderColor: TAlphaColor; - effBorderWidth: Single; - effDash: TStrokeDash; - isHovering: Boolean; - shouldIgnoreHover: Boolean; + Visualizer: IAstVisualizer; begin - inherited; + FCurrentAst := AstNode; - shouldIgnoreHover := False; - if Assigned(Node) then + if not FIsUndoing then begin - case Node.Kind of - akBlockExpression, akParameterList, akArgumentList, akExpressionList, akRecordFieldList: shouldIgnoreHover := True; - end; + FUndoStack.Clear; + FRedoStack.Clear; end; - isHovering := IsMouseOver and (not shouldIgnoreHover); + FFocusedNode := nil; - if FFrameless then + FWorkspace.World.DeleteChildren; + if Assigned(FRootViewNode) then + FRootViewNode.OnResized := nil; + FRootViewNode := nil; + + if not Assigned(AstNode) then + exit; + + Visualizer := TAstVisualizer.Create(FWorkspace, FWorkspace.World, 0); + FRootViewNode := Visualizer.CallAccept(AstNode); + + if Assigned(FRootViewNode) then begin - effBorderColor := FBorderColor; - effBorderWidth := FBorderWidth; - effDash := TStrokeDash.Dot; - end - else - begin - effBorderColor := FBorderColor; - effBorderWidth := FBorderWidth; - effDash := TStrokeDash.Solid; + FWorkspace.World.Size := FRootViewNode.Size; + FRootViewNode.OnResized := RootResized; + FRootViewNode.Position.Point := TPointF.Create(50, 50); end; - if isHovering then + if Assigned(FRootViewNode) then + ValidateAndShow; +end; + +procedure TAstEditor.CollectNodes(Parent: TControl); +var + i: Integer; + child: TControl; + viewNode: TAstViewNode; +begin + for i := 0 to Parent.ChildrenCount - 1 do begin - effBorderColor := TAlphaColors.White; - effBorderWidth := Max(2.0, FBorderWidth + 1.0); - effDash := TStrokeDash.Solid; - end; - - if FErrorMessage <> '' then - begin - effBorderColor := TAlphaColors.Red; - effBorderWidth := 2.0; - effDash := TStrokeDash.Solid; - end; - - Canvas.Fill.Kind := TBrushKind.Solid; - Canvas.Fill.Color := FBackgroundColor; - - if FFrameless and (FErrorMessage = '') and (not isHovering) then - begin - Canvas.FillRect(TRectF.Create(0, 0, Width, Height), 0, 0, [], 1.0); - - if FBorderWidth > 0 then + if Parent.Children[i] is TControl then begin - Canvas.Stroke.Kind := TBrushKind.Solid; - Canvas.Stroke.Color := effBorderColor; - Canvas.Stroke.Thickness := effBorderWidth; - Canvas.Stroke.Dash := effDash; - Canvas.DrawRect(TRectF.Create(0, 0, Width, Height), 0, 0, [], 1.0); + child := TControl(Parent.Children[i]); + + if child is TAstViewNode then + begin + viewNode := TAstViewNode(child); + + // HIER: Das Click-Event wiring + viewNode.OnMouseDown := HandleNodeMouseDown; + + if Assigned(viewNode.Node) and Assigned(viewNode.Node.Identity) then + begin + FNodeMap.AddOrSetValue(viewNode.Node.Identity, viewNode); + end; + end; + + if child.ChildrenCount > 0 then + CollectNodes(child); end; - end - else + end; +end; + +procedure TAstEditor.ClearVisuals; +begin + for var viewNode in FNodeMap.Values do begin - rect := TRectF.Create(0, 0, Width, Height); - if (effBorderWidth > 0) then - rect.Inflate(-effBorderWidth / 2, -effBorderWidth / 2); + viewNode.ErrorMessage := ''; + viewNode.TypeInfoText := ''; + end; +end; - Canvas.FillRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round); - - if (effBorderWidth > 0) and (effBorderColor <> TAlphaColors.Null) then +procedure TAstEditor.ApplyErrors(const Log: ICompilerLog); +var + entry: TCompilerError; + viewNode: TAstViewNode; +begin + for entry in Log.GetEntries do + begin + if (entry.Level = elError) and Assigned(entry.Node) and Assigned(entry.Node.Identity) then begin - Canvas.Stroke.Kind := TBrushKind.Solid; - Canvas.Stroke.Color := effBorderColor; - Canvas.Stroke.Thickness := effBorderWidth; - Canvas.Stroke.Dash := effDash; - Canvas.DrawRect(rect, FBorderRadius, FBorderRadius, AllCorners, 1.0, TCornerType.Round); + if FNodeMap.TryGetValue(entry.Node.Identity, viewNode) then + begin + if viewNode.ErrorMessage = '' then + viewNode.ErrorMessage := entry.Message + else + viewNode.ErrorMessage := viewNode.ErrorMessage + sLineBreak + entry.Message; + end; + end; + end; +end; + +procedure TAstEditor.ApplyTypes(const TypedAst: IAstNode); +var + propagator: TTypePropagator; +begin + if not Assigned(TypedAst) then + exit; + + propagator := TTypePropagator.Create(FNodeMap); + try + propagator.Accept(TypedAst); + finally + propagator.Free; + end; +end; + +procedure TAstEditor.RootResized(Sender: TObject); +begin + FWorkspace.World.Size := FRootViewNode.Size; +end; + +procedure TAstEditor.ValidateAndShow; +var + logicalAst: IAstNode; +begin + if not Assigned(FRootViewNode) then + exit; + + logicalAst := FRootViewNode.CreateAst; + + FNodeMap.Clear; + CollectNodes(FWorkspace.World); + + ClearVisuals; + + try + var log := TCompilerLog.Create as ICompilerLog; + + var expanded := FEnv.ExpandMacros(logicalAst); + var typedAst := FEnv.Bind(expanded, [], log); + var specAst := FEnv.Specialize(typedAst); + + ApplyTypes(specAst); + + if specAst.Kind = akLambdaExpression then + var compiled := FEnv.Link(specAst.AsLambdaExpression, log); + + ApplyErrors(log); + except + on E: ECompilationFailed do + begin + var tempLog := TCompilerLog.Create as ICompilerLog; + for var err in E.Errors do + tempLog.Add(err.Level, err.Message, err.Node); + ApplyErrors(tempLog); end; end; - if isHovering and (Parent is TAutoFitControl) then + FWorkspace.World.Repaint; +end; + +// ================================================================================================= +// == NAVIGATION IMPLEMENTATION +// ================================================================================================= + +procedure TAstEditor.SetFocusedNode(Node: TAstViewNode); +begin + if FFocusedNode = Node then + Exit; + + if Assigned(FFocusedNode) then + FFocusedNode.IsFocused := False; + + FFocusedNode := Node; + + if Assigned(FFocusedNode) then begin - Canvas.Fill.Color := TAlphaColors.DimGray; - Canvas.FillEllipse(TRectF.Create(4, 6, 8, 10), 1.0); - Canvas.FillEllipse(TRectF.Create(4, 12, 8, 16), 1.0); - Canvas.FillEllipse(TRectF.Create(4, 18, 8, 22), 1.0); + FFocusedNode.IsFocused := True; + EnsureVisible(FFocusedNode); end; end; -procedure TAstViewNode.SetBorderColor(const Value: TAlphaColor); +procedure TAstEditor.EnsureVisible(Node: TAstViewNode); +const + cMargin = 40; // Abstand zum Rand in Pixeln +var + NodeRect, ViewRect: TRectF; + Delta: TPointF; begin - if FBorderColor <> Value then + if (Node = nil) or (FWorkspace = nil) then + Exit; + + // 1. Hole absolute Koordinaten + NodeRect := Node.AbsoluteRect; + ViewRect := FWorkspace.AbsoluteRect; + + // 2. Definiere den "sicheren" Bereich (Viewport minus Rand) + ViewRect.Inflate(-cMargin, -cMargin); + + Delta := TPointF.Zero; + + // 3. Horizontal prüfen + if NodeRect.Width > ViewRect.Width then begin - FBorderColor := Value; - Repaint; + // Node ist breiter als der Screen -> Zentrieren + Delta.X := ViewRect.CenterPoint.X - NodeRect.CenterPoint.X; + end + else if NodeRect.Left < ViewRect.Left then + begin + // Node ragt links raus -> nach rechts schieben + Delta.X := ViewRect.Left - NodeRect.Left; + end + else if NodeRect.Right > ViewRect.Right then + begin + // Node ragt rechts raus -> nach links schieben + Delta.X := ViewRect.Right - NodeRect.Right; + end; + + // 4. Vertikal prüfen + if NodeRect.Height > ViewRect.Height then + begin + // Node ist höher als der Screen -> Zentrieren + Delta.Y := ViewRect.CenterPoint.Y - NodeRect.CenterPoint.Y; + end + else if NodeRect.Top < ViewRect.Top then + begin + // Node ragt oben raus -> nach unten schieben + Delta.Y := ViewRect.Top - NodeRect.Top; + end + else if NodeRect.Bottom > ViewRect.Bottom then + begin + // Node ragt unten raus -> nach oben schieben + Delta.Y := ViewRect.Bottom - NodeRect.Bottom; + end; + + // 5. Nur anwenden, wenn eine Verschiebung nötig ist + if not (SameValue(Delta.X, 0, TEpsilon.Position) and SameValue(Delta.Y, 0, TEpsilon.Position)) then + begin + FWorkspace.ContentOffset := FWorkspace.ContentOffset + Delta; end; end; -procedure TAstViewNode.SetBorderRadius(const Value: Single); +function TAstEditor.GetParentNode(StartNode: TAstViewNode): TAstViewNode; +var + curr: TControl; begin - if FBorderRadius <> Value then + Result := nil; + if StartNode = nil then + Exit; + + curr := StartNode.ParentControl; + while (curr <> nil) and (curr <> FWorkspace.World) do begin - FBorderRadius := Value; - Repaint; + if curr is TAstViewNode then + Exit(TAstViewNode(curr)); + curr := curr.ParentControl; end; end; -procedure TAstViewNode.SetBorderWidth(const Value: Single); -begin - if FBorderWidth <> Value then +function TAstEditor.GetFirstChild(StartNode: TAstViewNode): TAstViewNode; + function FindFirst(Parent: TControl): TAstViewNode; + var + i: Integer; begin - FBorderWidth := Value; - Repaint; + Result := nil; + for i := 0 to Parent.ChildrenCount - 1 do + begin + if Parent.Children[i] is TAstViewNode then + Exit(TAstViewNode(Parent.Children[i])) + else if (Parent.Children[i] is TControl) and (TControl(Parent.Children[i]).ChildrenCount > 0) then + begin + Result := FindFirst(TControl(Parent.Children[i])); + if Result <> nil then + Exit; + end; + end; + end; +begin + Result := FindFirst(StartNode); +end; + +function FindFirstViewNode(Container: TControl): TAstViewNode; +var + i: Integer; + Child: TControl; +begin + Result := nil; + if Container is TAstViewNode then + Exit(TAstViewNode(Container)); + + for i := 0 to Container.ChildrenCount - 1 do + begin + if Container.Children[i] is TControl then + begin + Child := TControl(Container.Children[i]); + if Child.Visible then + begin + Result := FindFirstViewNode(Child); + if Result <> nil then + Exit; + end; + end; end; end; -procedure TAstViewNode.SetFrameless(const Value: Boolean); +function FindLastViewNode(Container: TControl): TAstViewNode; +var + i: Integer; + Child: TControl; begin - if FFrameless <> Value then + Result := nil; + if Container is TAstViewNode then + exit(TAstViewNode(Container)); + + for i := Container.ChildrenCount - 1 downto 0 do begin - FFrameless := Value; - Repaint; + if Container.Children[i] is TControl then + begin + Child := TControl(Container.Children[i]); + if Child.Visible then + begin + var Found := FindLastViewNode(Child); + if Found <> nil then + Exit(Found); + end; + end; end; end; -procedure TAstViewNode.SetBackgroundColor(const Value: TAlphaColor); +// Helper um den nächsten ViewNode in einer Liste von Controls zu finden + +function FindNextSiblingInList(Parent: TControl; AfterControl: TControl): TAstViewNode; +var + i, StartIndex: Integer; + Candidate: TControl; begin - FBackgroundColor := Value; - Repaint; + Result := nil; + StartIndex := -1; + for i := 0 to Parent.ChildrenCount - 1 do + if Parent.Children[i] = AfterControl then + begin + StartIndex := i; + Break; + end; + + if StartIndex >= 0 then + begin + for i := StartIndex + 1 to Parent.ChildrenCount - 1 do + begin + if Parent.Children[i] is TControl then + begin + Candidate := TControl(Parent.Children[i]); + if Candidate.Visible then + begin + Result := FindFirstViewNode(Candidate); + if Result <> nil then + Exit; + end; + end; + end; + end; end; -procedure TAstViewNode.SetupNode; +function FindPrevSiblingInList(Parent: TControl; BeforeControl: TControl): TAstViewNode; +var + i, StartIndex: Integer; + Candidate: TControl; begin - if Assigned(FHandler) then - FHandler.BuildUI(Self); + Result := nil; + StartIndex := -1; + for i := 0 to Parent.ChildrenCount - 1 do + if Parent.Children[i] = BeforeControl then + begin + StartIndex := i; + Break; + end; + + if StartIndex >= 0 then + begin + for i := StartIndex - 1 downto 0 do + begin + if Parent.Children[i] is TControl then + begin + Candidate := TControl(Parent.Children[i]); + if Candidate.Visible then + begin + Result := FindLastViewNode(Candidate); + if Result <> nil then + Exit; + end; + end; + end; + end; end; -{ TBaseNodeHandler } +// --- Vertical Axis Navigation (Skip Row) --- -constructor TBaseNodeHandler.Create(const ANode: T); +function TAstEditor.GetNextVerticalNeighbor(StartNode: TAstViewNode): TAstViewNode; +var + Current, Parent: TControl; +begin + Result := nil; + Current := StartNode; + + while (Current <> nil) and (Current <> FWorkspace.World) do + begin + Parent := Current.ParentControl; + if Parent = nil then + Break; + + // Wenn der Parent vertikal ist, suchen wir hier den Nachbarn. + if (Parent is TAutoFitControl) and (TAutoFitControl(Parent).Orientation = TLayoutOrientation.loVertical) then + begin + Result := FindNextSiblingInList(Parent, Current); + if Result <> nil then + Exit; + end; + + Current := Parent; + end; + + if Result = nil then + Result := GetNextLinearNode(StartNode) +end; + +function TAstEditor.GetPrevVerticalNeighbor(StartNode: TAstViewNode): TAstViewNode; +var + Current, Parent: TControl; +begin + Result := nil; + Current := StartNode; + + while (Current <> nil) and (Current <> FWorkspace.World) do + begin + Parent := Current.ParentControl; + if Parent = nil then + Break; + + if (Parent is TAutoFitControl) and (TAutoFitControl(Parent).Orientation = TLayoutOrientation.loVertical) then + begin + Result := FindPrevSiblingInList(Parent, Current); + if Result <> nil then + Exit; + end; + + Current := Parent; + end; + + if Result = nil then + Result := GetPrevLinearNode(StartNode) +end; + +function TAstEditor.GetNextLinearNode(StartNode: TAstViewNode): TAstViewNode; +var + CurrentControl, ParentControl: TControl; + i, StartIndex: Integer; + Candidate: TControl; +begin + // 1. Try Child (Drill Down) + Result := GetFirstChild(StartNode); + if Result <> nil then + Exit; + + // 2. Walk up visually and check for next siblings at every layout level + CurrentControl := StartNode; + while (CurrentControl <> nil) and (CurrentControl <> FWorkspace.World) do + begin + ParentControl := CurrentControl.ParentControl; + if ParentControl = nil then + Break; + + // Find index of current control + StartIndex := -1; + for i := 0 to ParentControl.ChildrenCount - 1 do + if ParentControl.Children[i] = CurrentControl then + begin + StartIndex := i; + Break; + end; + + // Scan forward from next index + if StartIndex >= 0 then + begin + for i := StartIndex + 1 to ParentControl.ChildrenCount - 1 do + begin + if ParentControl.Children[i] is TControl then + begin + Candidate := TControl(ParentControl.Children[i]); + if Candidate.Visible then + begin + // Check if this candidate IS a node or CONTAINS a node + Result := FindFirstViewNode(Candidate); + if Result <> nil then + Exit; + end; + end; + end; + end; + + // Move up one physical level (e.g. from ParameterList to TitleContainer) + CurrentControl := ParentControl; + end; + + Result := nil; +end; + +function TAstEditor.GetPrevLinearNode(StartNode: TAstViewNode): TAstViewNode; +var + CurrentControl, ParentControl: TControl; + i, StartIndex: Integer; + Candidate: TControl; +begin + // No simple "Drill Down" here, because Prev means "Left of me". + // If I have children, "Left of me" is OUTSIDE of me (Parent or Sibling). + // Wait: Standard Tab behavior: + // Tab: Current -> Child -> Next Sibling + // Shift-Tab: Current -> Prev Sibling (Deepest Child) -> Parent + + CurrentControl := StartNode; + while (CurrentControl <> nil) and (CurrentControl <> FWorkspace.World) do + begin + ParentControl := CurrentControl.ParentControl; + if ParentControl = nil then + Break; + + // Find index + StartIndex := -1; + for i := 0 to ParentControl.ChildrenCount - 1 do + if ParentControl.Children[i] = CurrentControl then + begin + StartIndex := i; + Break; + end; + + // Scan backward + if StartIndex >= 0 then + begin + for i := StartIndex - 1 downto 0 do + begin + if ParentControl.Children[i] is TControl then + begin + Candidate := TControl(ParentControl.Children[i]); + if Candidate.Visible then + begin + // Find the deepest last node inside this sibling + Result := FindLastViewNode(Candidate); + if Result <> nil then + Exit; + end; + end; + end; + end; + + // If no sibling found to the left, the Parent is the previous logical node + if ParentControl is TAstViewNode then + Exit(TAstViewNode(ParentControl)); + + // If Parent is just a Layout Container, keep walking up + CurrentControl := ParentControl; + end; + + Result := nil; +end; + +// --- Main Navigate Method --- + +procedure TAstEditor.Navigate(Key: Word; Shift: TShiftState); +var + Current: TAstViewNode; + Target: TAstViewNode; +begin + if FFocusedNode = nil then + begin + SetFocusedNode(FRootViewNode); + Exit; + end; + + Current := FFocusedNode; + Target := nil; + + // 1. Linear Navigation (Tab) - Bleibt wie gehabt + if Key = vkTab then + begin + if ssShift in Shift then + Target := GetPrevLinearNode(Current) + else + Target := GetNextLinearNode(Current); + + if Target <> nil then + SetFocusedNode(Target); + Exit; + end; + + // 2. Escape - Bleibt "Notausgang" zum Parent + if Key = vkEscape then + begin + Target := GetParentNode(Current); + if Target <> nil then + SetFocusedNode(Target); + Exit; + end; + + // 3. Axis Analysis + case Key of + vkLeft: + begin + Target := GetPrevLinearNode(Current); + end; + + vkRight: + begin + Target := GetNextLinearNode(Current); + end; + + vkUp: + begin + Target := GetPrevVerticalNeighbor(Current) + end; + + vkDown: + begin + Target := GetNextVerticalNeighbor(Current) + end; + end; + + if Target <> nil then + SetFocusedNode(Target); +end; + +{ TAstEditor.TTypePropagator } + +constructor TAstEditor.TTypePropagator.Create(AMap: TNodeMap); begin inherited Create; - FNode := ANode; + FMap := AMap; end; -function TBaseNodeHandler.GetAstNode: IAstNode; +procedure TAstEditor.TTypePropagator.ApplyType(const Node: IAstNode); +var + viewNode: TAstViewNode; begin - Result := FNode; + if Assigned(Node) and Assigned(Node.Identity) then + begin + if FMap.TryGetValue(Node.Identity, viewNode) then + begin + if Node.IsTyped then + begin + var st := Node.AsTypedNode.StaticType; + if Assigned(st) then + viewNode.TypeInfoText := st.ToString; + end; + end; + end; +end; + +procedure TAstEditor.TTypePropagator.Accept(const Node: IAstNode); +begin + if Node = nil then + exit; + ApplyType(Node); + Node.Accept(Self); end; end.