From 0e0100643798b7e678991f863086027e2dc314f0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Wed, 22 Sep 2021 14:36:12 +0000 Subject: [PATCH] wip --- CrowEditBase/CrowEditBase.csproj | 4 +- CrowEditBase/src/Compiler/SourceDocument.cs | 4 +- CrowEditBase/src/CrowEditBase.cs | 141 ++++++++++++++++-- CrowEditBase/src/CrowEditComponent.cs | 3 +- CrowEditBase/src/Document.cs | 26 +++- CrowEditBase/src/Editor.cs | 106 +++++++------ CrowEditBase/src/Plugin.cs | 10 +- CrowEditBase/src/PluginsLoadContext.cs | 6 +- CrowEditBase/src/Project.cs | 35 ++--- CrowEditBase/src/SourceEditor.cs | 127 ++++++++++++---- CrowEditBase/src/TextDocument.cs | 4 +- CrowEditBase/src/TreeNode.cs | 36 +++-- CrowEditBase/src/VirtualNode.cs | 2 +- CrowEditBase/ui/CrowEdit.style | 9 +- CrowEditBase/ui/TreeExpandable.template | 38 +++-- Directory.Build.props | 2 +- plugins/CECrowPlugin/default.conf | 2 +- plugins/CECrowPlugin/src/CrowService.cs | 2 +- plugins/CECrowPlugin/src/DebugInterface.cs | 2 - plugins/CECrowPlugin/src/ImlDocument.cs | 2 +- .../src/{StyleParsing => }/StyleDocument.cs | 10 +- .../CECrowPlugin/ui/DbgEventTreeItems.itemp | 4 +- plugins/CECrowPlugin/ui/winCrowPreview.crow | 16 +- plugins/CERoslynPlugin/src/CELogger.cs | 59 +++----- plugins/CERoslynPlugin/src/CETaskLogHook.cs | 6 +- plugins/CERoslynPlugin/src/CSDocument.cs | 14 +- plugins/CERoslynPlugin/src/MSBuildProject.cs | 56 ++++--- .../src/ProjectTree/ProjectItemNodes.cs | 15 +- .../src/ProjectTree/ProjectNode.cs | 24 --- plugins/CERoslynPlugin/src/SolutionProject.cs | 15 +- .../ui/MSBuildProjectNode.template | 2 +- .../CEXmlPlugin/src/Parsing/XmlDocument.cs | 2 +- plugins/Directory.Build.props | 4 +- src/CrowEdit.cs | 19 ++- ui/windows/winEditor.crow | 55 ++++--- ui/windows/winFileExplorer.crow | 2 +- ui/windows/winProjects.crow | 26 ++-- 37 files changed, 564 insertions(+), 326 deletions(-) rename plugins/CECrowPlugin/src/{StyleParsing => }/StyleDocument.cs (88%) delete mode 100644 plugins/CERoslynPlugin/src/ProjectTree/ProjectNode.cs diff --git a/CrowEditBase/CrowEditBase.csproj b/CrowEditBase/CrowEditBase.csproj index f9fd2fa..2e5c287 100644 --- a/CrowEditBase/CrowEditBase.csproj +++ b/CrowEditBase/CrowEditBase.csproj @@ -15,7 +15,7 @@ - - + + diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index f023014..25d5396 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -11,8 +11,8 @@ using System.Collections; namespace CrowEditBase { public abstract class SourceDocument : TextDocument { - public SourceDocument (string fullPath) - : base (fullPath) { + public SourceDocument (string fullPath, string editorPath = "#ui.sourceEditor.itmp") + : base (fullPath, editorPath) { } protected Token[] tokens; protected SyntaxNode RootNode; diff --git a/CrowEditBase/src/CrowEditBase.cs b/CrowEditBase/src/CrowEditBase.cs index b40fa18..a34ec0f 100644 --- a/CrowEditBase/src/CrowEditBase.cs +++ b/CrowEditBase/src/CrowEditBase.cs @@ -9,14 +9,13 @@ using Crow; using System.Runtime.CompilerServices; using System.Collections.Generic; using System.Runtime.Loader; +using System.Text; namespace CrowEditBase { public abstract class CrowEditBase : Interface { - protected class DocumentClientClassList : List { - string defaultClass; - } - protected Dictionary FileAssociations = new Dictionary (); + protected Dictionary> FileAssociations = new Dictionary> (); + protected Dictionary> SupportedEditors = new Dictionary> (); ObservableList logs = new ObservableList(); public ObservableList MainLog => logs; @@ -31,10 +30,10 @@ namespace CrowEditBase public void AddFileAssociation (string extension, Type clientClass) { if (!FileAssociations.ContainsKey (extension)) - FileAssociations.Add (extension, new DocumentClientClassList ()); + FileAssociations.Add (extension, new List ()); if (!FileAssociations[extension].Contains (clientClass)) FileAssociations[extension].Add (clientClass); - + NotifyValueChanged ("EditorItemTemplates", (object)EditorItemTemplates); } public void RemoveFileAssociationByType (Type clientClass) { @@ -44,6 +43,18 @@ namespace CrowEditBase clientType = FileAssociations.ContainsKey (extension) ? FileAssociations[extension].FirstOrDefault () : null; return clientType != null; } + public void AddSupportedEditor (Type clientClass, string editorPath) { + if (!SupportedEditors.ContainsKey (clientClass)) + SupportedEditors.Add (clientClass, new List ()); + if (!SupportedEditors[clientClass].Contains (editorPath)) + SupportedEditors[clientClass].Add (editorPath); + NotifyValueChanged ("EditorItemTemplates", (object)EditorItemTemplates); + } + public bool TryGetDefaultEditorForDocumentType (Type clientType, out string editorPath) { + editorPath = SupportedEditors.ContainsKey (clientType) ? SupportedEditors[clientType].FirstOrDefault () : null; + return editorPath != null; + } + public static CrowEditBase App; @@ -84,7 +95,7 @@ namespace CrowEditBase //TODO:flattened project public IEnumerable FlattenProjects { get { - foreach (var node in Projects.SelectMany (child => child.Flatten)) + foreach (var node in Projects.SelectMany (child => child.FlattenSubProjetcs)) yield return node; } } @@ -100,7 +111,16 @@ namespace CrowEditBase containingProject = FlattenProjects.FirstOrDefault (p => p.ContainsFile (fullPath)); return containingProject != null; } - + public bool TryFindFileNode (string fullPath, out IFileNode node) { + foreach (Project prj in Projects) { + if (prj.TryFindFileNode (fullPath, out IFileNode n)) { + node = n; + return true; + } + } + node = null; + return false; + } public Document CurrentDocument { get => currentDocument; @@ -108,7 +128,8 @@ namespace CrowEditBase if (currentDocument == value) return; - currentDocument?.UnselectDocument (); + if (currentDocument != null) + currentDocument.IsSelected = false; currentDocument = value; NotifyValueChanged (currentDocument); @@ -116,7 +137,7 @@ namespace CrowEditBase if (currentDocument == null) return; - currentDocument.SelectDocument (); + currentDocument.IsSelected = true; FileCommands[2] = currentDocument.CMDSave; FileCommands[3] = currentDocument.CMDSaveAs; EditCommands[0] = currentDocument.CMDUndo; @@ -185,6 +206,10 @@ namespace CrowEditBase public bool IsOpened (string filePath) => string.IsNullOrEmpty (filePath) ? false : OpenedDocuments.Any (d => d.FullPath == filePath); + public bool TryGetOpenedDocument (string fullPath, out Document doc) { + doc = OpenedDocuments.FirstOrDefault (d => d.FullPath == fullPath); + return doc != null; + } public Document OpenFile (string filePath) { if (string.IsNullOrEmpty (filePath)) @@ -210,7 +235,7 @@ namespace CrowEditBase openOrCreateFile (Path.Combine (CurFileDir, _defaultFileName)); } - protected abstract Document openOrCreateFile (string filePath); + protected abstract Document openOrCreateFile (string filePath, string editorPath = null); public void CloseDocument (Document doc) { if (doc == null) return; @@ -271,5 +296,99 @@ namespace CrowEditBase } } + + #region Editor item templates + public string EditorItemTemplates { + get { + StringBuilder sb = new StringBuilder (1024); + sb.Append (defaultEditorITemps); + foreach (string editorPath in SupportedEditors.Values.SelectMany (a=>a).Distinct ()) + sb.Append ($""); + return sb.ToString (); + } + } + string defaultEditorITemps = @" + + + + + + + + + + + + + + + + "; + #endregion + + +#region main options + public virtual Color MarginBackground { + get => Configuration.Global.Get ("MarginBackground", Colors.Onyx); + set { + if (value == MarginBackground) + return; + Configuration.Global.Set ("MarginBackground", value); + NotifyValueChanged ("MarginBackground", value); + + CurrentEditor?.RegisterForRedraw (); + } + } + public bool PrintLineNumbers { + get => Configuration.Global.Get ("PrintLineNumbers", true); + set { + if (PrintLineNumbers == value) + return; + Configuration.Global.Set ("PrintLineNumbers", value); + NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers); + + CurrentEditor?.RegisterForGraphicUpdate (); + } + } + //Folding + public bool FoldingEnabled { + get => Crow.Configuration.Global.Get ("FoldingEnabled", true); + set { + if (FoldingEnabled == value) + return; + Crow.Configuration.Global.Set ("FoldingEnabled", value); + NotifyValueChanged (value); + } + } + public bool AutoFoldRegions { + get => Crow.Configuration.Global.Get ("AutoFoldRegions", true); + set { + if (AutoFoldRegions == value) + return; + Crow.Configuration.Global.Set ("AutoFoldRegions", value); + NotifyValueChanged (value); + } + } + public bool AutoFoldComments { + get => Crow.Configuration.Global.Get ("AutoFoldComments", true); + set { + if (AutoFoldComments == value) + return; + Crow.Configuration.Global.Set ("AutoFoldComments", value); + NotifyValueChanged (value); + } + } + +#endregion } } \ No newline at end of file diff --git a/CrowEditBase/src/CrowEditComponent.cs b/CrowEditBase/src/CrowEditComponent.cs index d3995a6..b1a5c30 100644 --- a/CrowEditBase/src/CrowEditComponent.cs +++ b/CrowEditBase/src/CrowEditComponent.cs @@ -23,7 +23,7 @@ namespace CrowEditBase #endregion #region ISelectable implementation - bool isSelected; + protected bool isSelected; public event EventHandler Selected; public event EventHandler Unselected; @@ -34,7 +34,6 @@ namespace CrowEditBase return; isSelected = value; - NotifyValueChanged (isSelected); } } diff --git a/CrowEditBase/src/Document.cs b/CrowEditBase/src/Document.cs index 472cbd7..8b3343e 100644 --- a/CrowEditBase/src/Document.cs +++ b/CrowEditBase/src/Document.cs @@ -13,15 +13,20 @@ using static CrowEditBase.CrowEditBase; namespace CrowEditBase { public abstract class Document : CrowEditComponent { - protected Project project; - public Document (string fullPath) { + public Document (string fullPath, string editorPath) { initCommands (); + EditorPath = editorPath; FullPath = fullPath; - App.TryGetContainingProject (FullPath, out project); } + /// + /// Editor used to open the document, can't be changed once opened + /// + /// + /// The editor path is used as an ID for itemTemplate selection + /// + /// + public string EditorPath { get; private set; }//the ressource path is used as an id for editor template selection. public event EventHandler CloseEvent; - public void SelectDocument () => IsSelected = true; - public void UnselectDocument () => IsSelected = true; protected ReaderWriterLockSlim editorRWLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); public void EnterReadLock () => editorRWLock.EnterReadLock (); @@ -102,5 +107,16 @@ namespace CrowEditBase public abstract bool IsDirty { get; } public override string ToString() => FullPath; + + public override bool IsSelected { + get => base.IsSelected; + set { + if (isSelected == value) + return; + base.IsSelected = value; + if (App.TryFindFileNode (FullPath, out IFileNode node)) + (node as TreeNode).IsSelected = isSelected; + } + } } } \ No newline at end of file diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs index 92eb65b..1d3fdc1 100644 --- a/CrowEditBase/src/Editor.cs +++ b/CrowEditBase/src/Editor.cs @@ -7,11 +7,7 @@ using Glfw; using Crow.Text; using System.Collections.Generic; using Crow.Drawing; -using System.Threading.Tasks; using System.Linq; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Collections; using CrowEditBase; using System.Threading; using System.ComponentModel; @@ -357,7 +353,7 @@ namespace Crow CharLocation selStart = default, selEnd = default; bool selectionNotEmpty = false; - if (HasFocus) { + //if (HasFocus) { if (currentLoc?.Column < 0) { updateLocation (gr, cb.Width, ref currentLoc); NotifyValueChanged ("CurrentColumn", CurrentColumn); @@ -384,7 +380,7 @@ namespace Crow } } else IFace.forceTextCursor = true; - } + //} if (!string.IsNullOrEmpty (_text)) { Foreground?.SetAsSource (IFace, gr); @@ -427,7 +423,7 @@ namespace Crow Foreground.SetAsSource (IFace, gr); ********** DEBUG TextLineCollection *************/ - if (HasFocus && selectionNotEmpty) { + if (selectionNotEmpty) { RectangleD selRect = lineRect; if (i >= selStart.Line && i <= selEnd.Line) { @@ -472,14 +468,14 @@ namespace Crow protected virtual void updateHoverLocation (Point mouseLocalPos) { int hoverLine = (int)Math.Min (Math.Max (0, Math.Floor ((mouseLocalPos.Y + ScrollY)/ (fe.Ascent + fe.Descent))), lines.Count - 1); int scrollLine = (int)Math.Ceiling((double)ScrollY / (fe.Ascent + fe.Descent)); - if (hoverLine > scrollLine + visibleLines) - ScrollY = (int)((double)(hoverLine - visibleLines) * (fe.Ascent + fe.Descent)); + /*if (hoverLine > scrollLine + visibleLines) + ScrollY = (int)((double)(hoverLine - visibleLines) * (fe.Ascent + fe.Descent));*/ NotifyValueChanged("MouseY", mouseLocalPos.Y + ScrollY); NotifyValueChanged("ScrollY", ScrollY); NotifyValueChanged("VisibleLines", visibleLines); NotifyValueChanged("HoverLine", hoverLine); NotifyValueChanged("ScrollLine", hoverLine); - hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X); + hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X + ScrollX); using (Context gr = new Context (IFace.surf)) { setFontForContext (gr); updateLocation (gr, ClientRectangle.Width, ref hoverLoc); @@ -571,8 +567,8 @@ namespace Crow } } - protected void checkShift () { - if (IFace.Shift) { + protected void checkShift (KeyEventArgs e) { + if (e.Modifiers.HasFlag (Modifier.Shift)) { if (!selectionStart.HasValue) selectionStart = CurrentLoc; } else @@ -599,18 +595,20 @@ namespace Crow public override int measureRawSize(LayoutingType lt) { DbgLogger.StartEvent(DbgEvtType.GOMeasure, this, lt); + try { + if ((bool)lines?.IsEmpty) + getLines (); - if ((bool)lines?.IsEmpty) - getLines (); - - if (!textMeasureIsUpToDate) { - using (Context gr = new Context (IFace.surf)) { - setFontForContext (gr); - measureTextBounds (gr); + if (!textMeasureIsUpToDate) { + using (Context gr = new Context (IFace.surf)) { + setFontForContext (gr); + measureTextBounds (gr); + } } + return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width); + } finally { + DbgLogger.EndEvent(DbgEvtType.GOMeasure); } - DbgLogger.EndEvent(DbgEvtType.GOMeasure); - return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width); } protected override void onDraw (Context gr) @@ -643,33 +641,41 @@ namespace Crow { base.onFocused (sender, e); - if (CurrentLoc == null) { - selectionStart = new CharLocation (0, 0); - CurrentLoc = new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length); - } + if (CurrentLoc == null) + CurrentLoc = new CharLocation (0, 0); RegisterForRedraw (); (IFace as CrowEditBase.CrowEditBase).CurrentEditor = this; } - protected override void onUnfocused (object sender, EventArgs e) + /*protected override void onUnfocused (object sender, EventArgs e) { base.onUnfocused (sender, e); RegisterForRedraw (); - } + }*/ public override void onMouseEnter (object sender, MouseMoveEventArgs e) { base.onMouseEnter (sender, e); + HasFocus = true; if (Focusable) IFace.MouseCursor = MouseCursor.ibeam; } public override void onMouseMove (object sender, MouseMoveEventArgs e) { base.onMouseMove (sender, e); - - updateHoverLocation (ScreenPointToLocal (e.Position)); + mouseMove (e); + } + public override void onMouseWheel(object sender, MouseWheelEventArgs e) + { + base.onMouseWheel(sender, e); + mouseMove (e); + } + void mouseMove (MouseEventArgs e) { + updateHoverLocation (ScreenPointToLocal (IFace.MousePosition)); if (HasFocus && IFace.IsDown (MouseButton.Left)) { CurrentLoc = hoverLoc; + autoAdjustScroll = true; + IFace.forceTextCursor = true; RegisterForRedraw (); } } @@ -745,9 +751,9 @@ namespace Crow } break; case Key.Insert: - if (IFace.Shift) + if (e.Modifiers.HasFlag (Modifier.Shift)) Paste (); - else if (IFace.Ctrl) + else if (e.Modifiers.HasFlag (Modifier.Control)) Copy (); break; case Key.KeypadEnter: @@ -765,56 +771,62 @@ namespace Crow update (new TextChange (selection.Start, selection.Length, "\t")); break; case Key.PageUp: - checkShift (); + checkShift (e); LineMove (-visibleLines); RegisterForRedraw (); break; case Key.PageDown: - checkShift (); + checkShift (e); LineMove (visibleLines); RegisterForRedraw (); break; case Key.Home: targetColumn = -1; - checkShift (); - if (IFace.Ctrl) + checkShift (e); + if (e.Modifiers.HasFlag (Modifier.Control)) CurrentLoc = new CharLocation (0, 0); else CurrentLoc = new CharLocation (CurrentLoc.Value.Line, 0); RegisterForRedraw (); break; case Key.End: - checkShift (); - int l = IFace.Ctrl ? lines.Count - 1 : CurrentLoc.Value.Line; + checkShift (e); + int l = e.Modifiers.HasFlag (Modifier.Control) ? lines.Count - 1 : CurrentLoc.Value.Line; CurrentLoc = new CharLocation (l, lines[l].Length); RegisterForRedraw (); break; case Key.Left: - checkShift (); - if (IFace.Ctrl) + checkShift (e); + if (e.Modifiers.HasFlag (Modifier.Control)) GotoWordStart (); else MoveLeft (); RegisterForRedraw (); break; case Key.Right: - checkShift (); - if (IFace.Ctrl) + checkShift (e); + if (e.Modifiers.HasFlag (Modifier.Control)) GotoWordEnd (); else MoveRight (); RegisterForRedraw (); break; case Key.Up: - checkShift (); + checkShift (e); LineMove (-1); RegisterForRedraw (); break; case Key.Down: - checkShift (); + checkShift (e); LineMove (1); RegisterForRedraw (); break; + case Key.A: + if (e.Modifiers.HasFlag (Modifier.Control)) { + selectionStart = new CharLocation (0, 0); + CurrentLoc = new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length); + } + break; default: base.onKeyDown (sender, e); return; @@ -836,10 +848,10 @@ namespace Crow if (autoAdjustScroll) { autoAdjustScroll = false; int goodMsrs = 0; - if (cursor.Right < 0) - ScrollX += cursor.Right; + if (cursor.Left < 0) + ScrollX += cursor.Left; else if (cursor.X > cb.Width) - ScrollX += cursor.X - cb.Width; + ScrollX += cursor.X - cb.Width + 5; else goodMsrs++; @@ -858,7 +870,7 @@ namespace Crow return cursor; } - void updateMaxScrolls (LayoutingType layout) { + protected virtual void updateMaxScrolls (LayoutingType layout) { Rectangle cb = ClientRectangle; if (layout == LayoutingType.Width) { MaxScrollX = cachedTextSize.Width - cb.Width; diff --git a/CrowEditBase/src/Plugin.cs b/CrowEditBase/src/Plugin.cs index e9f0dd1..e681c80 100644 --- a/CrowEditBase/src/Plugin.cs +++ b/CrowEditBase/src/Plugin.cs @@ -74,9 +74,13 @@ namespace CrowEditBase { foreach (string associations in fileAssociations.Split (';')) { string[] typeExts = associations.Split (':'); - Type clientClass = loadContext.MainAssembly.GetType (typeExts[0]); - foreach (string ext in typeExts[1].Split (',')) - App.AddFileAssociation (ext, clientClass); + Type clientClass = loadContext.MainAssembly.GetType (typeExts[0].Trim()); + foreach (string ext in typeExts[1].Split (','))//supported extension comma separated list + App.AddFileAssociation (ext.Trim(), clientClass); + if (typeExts.Length < 3) + continue; + foreach (string editorPath in typeExts[2].Split (','))//comma separated list of supported editor path. + App.AddSupportedEditor (clientClass, editorPath.Trim()); } } catch (System.Exception ex) { diff --git a/CrowEditBase/src/PluginsLoadContext.cs b/CrowEditBase/src/PluginsLoadContext.cs index 6827839..be4fd16 100644 --- a/CrowEditBase/src/PluginsLoadContext.cs +++ b/CrowEditBase/src/PluginsLoadContext.cs @@ -13,7 +13,7 @@ using static CrowEditBase.CrowEditBase; namespace CrowEditBase { - + /*public class Plugin { string path; Assembly assembly; @@ -35,8 +35,8 @@ namespace CrowEditBase string pluginAssembly = Path.Combine (fullPath, $"{Name}.dll"); MainAssembly = LoadFromAssemblyPath (pluginAssembly); } - protected override Assembly Load(AssemblyName assemblyName) { - string assemblyPath = Path.Combine (fullPath, assemblyName.Name + ".dll"); + protected override Assembly Load(AssemblyName assemblyName) { + string assemblyPath = Path.Combine (fullPath, assemblyName.Name + ".dll"); return File.Exists (assemblyPath) ? LoadFromAssemblyPath (assemblyPath) : null; } diff --git a/CrowEditBase/src/Project.cs b/CrowEditBase/src/Project.cs index 1c9bb93..0b7c049 100644 --- a/CrowEditBase/src/Project.cs +++ b/CrowEditBase/src/Project.cs @@ -2,40 +2,29 @@ // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.IO; +using System.Collections.Generic; using System.Linq; -using System.Threading; using Crow; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Collections.Generic; using static CrowEditBase.CrowEditBase; namespace CrowEditBase { - public abstract class Project : CrowEditComponent { + public abstract class Project : TreeNode { bool isLoaded; protected Project parent; - protected IList subProjects; - - public Project Parent => parent; - public IList SubProjects => subProjects; - public IEnumerable Flatten { + public abstract bool ContainsFile (string fullPath); + public IEnumerable SubProjetcs => Childs.OfType (); + public IEnumerable FlattenSubProjetcs { get { - yield return this; - if (subProjects != null) { - foreach (var node in subProjects?.SelectMany (child => child.Flatten)) - yield return node; - } + foreach (var node in SubProjetcs.SelectMany (sp => sp.FlattenSubProjetcs)) + yield return node; } } - public virtual bool ContainsFile (string fullPath) => false; - public bool HasChildren => subProjects?.Count > 0; - public string FullPath { get ; private set; } public abstract string Name { get; } - public string Caption => Name; + public override string Caption => Name; + public override NodeType NodeType => NodeType.Project; + public bool IsLoaded { get => isLoaded; set { @@ -55,7 +44,7 @@ namespace CrowEditBase FullPath = fullPath; } public Command CMDLoad, CMDUnload, CMDReload, CMDClose; - public virtual CommandGroup Commands => new CommandGroup ( + public override CommandGroup Commands => new CommandGroup ( CMDLoad, CMDUnload, CMDReload, CMDClose); void initCommands () { @@ -75,6 +64,6 @@ namespace CrowEditBase App.Projects.Remove (this); IsLoaded = false; } - public virtual string Icon => "#icons.question.svg"; + public override string Icon => "#icons.question.svg"; } } \ No newline at end of file diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs index a20f683..aaa93a4 100644 --- a/CrowEditBase/src/SourceEditor.cs +++ b/CrowEditBase/src/SourceEditor.cs @@ -5,23 +5,16 @@ using System; using Glfw; using Crow.Text; -using System.Collections.Generic; using Crow.Drawing; -using System.Threading.Tasks; -using System.Linq; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Collections; using CrowEditBase; +using static CrowEditBase.CrowEditBase; namespace Crow { public class SourceEditor : Editor { object TokenMutex = new object(); - - - ListBox overlay; IList suggestions; volatile bool disableSuggestions; @@ -53,8 +46,6 @@ namespace Crow //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}"); } - - protected void tryGetSuggestions () { if (currentLoc.HasValue && Document is SourceDocument srcDoc) Suggestions = srcDoc.GetSuggestions (lines.GetAbsolutePosition (CurrentLoc.Value)); @@ -121,7 +112,6 @@ namespace Crow hideOverlay (); base.onMouseDown (sender, e); } - public override void onKeyDown(object sender, KeyEventArgs e) { TextSpan selection = Selection; @@ -188,22 +178,87 @@ namespace Crow base.onKeyDown(sender, e); } + const int leftMarginGap = 3;//gap between margin start and numbering + const int leftMarginRightGap = 3;//gap between items in margin and text + const int foldSize = 9;//folding rectangles size + const int foldMargin = 9; + + int leftMargin; + void updateMargin () { + leftMargin = leftMarginGap; + if (App.PrintLineNumbers) + leftMargin += (int)Math.Ceiling((double)lines.Count.ToString().Length * fe.MaxXAdvance) + 6; + if (App.FoldingEnabled) + leftMargin += foldMargin; + if (leftMargin > 0) + leftMargin += leftMarginRightGap; + //updateVisibleColumns (); + } + public override int measureRawSize(LayoutingType lt) + { + DbgLogger.StartEvent(DbgEvtType.GOMeasure, this, lt); + try { + if ((bool)lines?.IsEmpty) + getLines (); + + updateMargin (); + + if (!textMeasureIsUpToDate) { + using (Context gr = new Context (IFace.surf)) { + setFontForContext (gr); + measureTextBounds (gr); + } + } + return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width + leftMargin); + } finally { + DbgLogger.EndEvent(DbgEvtType.GOMeasure); + } + } + protected override void updateMaxScrolls (LayoutingType layout) { + updateMargin(); + Rectangle cb = ClientRectangle; + cb.Width -= leftMargin; + if (layout == LayoutingType.Width) { + MaxScrollX = cachedTextSize.Width - cb.Width; + NotifyValueChanged ("PageWidth", ClientRectangle.Width); + if (cachedTextSize.Width > 0) + NotifyValueChanged ("ChildWidthRatio", Math.Min (1.0, (double)cb.Width / cachedTextSize.Width)); + } else if (layout == LayoutingType.Height) { + MaxScrollY = cachedTextSize.Height - cb.Height; + NotifyValueChanged ("PageHeight", ClientRectangle.Height); + if (cachedTextSize.Height > 0) + NotifyValueChanged ("ChildHeightRatio", Math.Min (1.0, (double)cb.Height / cachedTextSize.Height)); + } + } + protected override void drawContent (Context gr) { - if (!(Document is SourceDocument xmlDoc)) { + if (!(Document is SourceDocument doc)) { base.drawContent (gr); return; } //lock(TokenMutex) { - xmlDoc.EnterReadLock (); + doc.EnterReadLock (); try { - if (xmlDoc.Tokens == null || xmlDoc.Tokens.Length == 0) { + if (doc.Tokens == null || doc.Tokens.Length == 0) { base.drawContent (gr); return; } - Rectangle cb = ClientRectangle; - fe = gr.FontExtents; double lineHeight = fe.Ascent + fe.Descent; + updateMargin (); + + bool printLineNumbers = App.PrintLineNumbers; + Color marginBG = App.MarginBackground; + Color marginFG = Colors.Ivory; + double lineNumWidth = gr.TextExtents (lines.Count.ToString()).Width; + + Rectangle cb = ClientRectangle; + RectangleD marginRect = new RectangleD (cb.X, cb.Y, leftMargin - leftMarginRightGap, lineHeight); + /*gr.SetSource (App.MarginBackground); + gr.Rectangle (marginRect); + gr.Fill ();*/ + cb.Left += leftMargin; + CharLocation selStart = default, selEnd = default; bool selectionNotEmpty = false; @@ -252,16 +307,17 @@ namespace Crow int x = 0, y = 0; double pixX = cb.Left; - Foreground.SetAsSource (IFace, gr); gr.Translate (-ScrollX, -ScrollY); - ReadOnlySpan sourceBytes = xmlDoc.Source.AsSpan(); + + + ReadOnlySpan sourceBytes = doc.Source.AsSpan(); Span bytes = stackalloc byte[128]; TextExtents extents; int tokPtr = 0; - Token tok = xmlDoc.Tokens[tokPtr]; + Token tok = doc.Tokens[tokPtr]; bool multilineToken = false; ReadOnlySpan buff = sourceBytes; @@ -286,7 +342,7 @@ namespace Crow } else buff = sourceBytes.Slice (tok.Start, tok.Length); - gr.SetSource(xmlDoc.GetColorForToken (tok.Type)); + gr.SetSource(doc.GetColorForToken (tok.Type)); } int size = buff.Length * 4 + 1; @@ -311,13 +367,13 @@ namespace Crow break; } - if (++tokPtr >= xmlDoc.Tokens.Length) + if (++tokPtr >= doc.Tokens.Length) break; - tok = xmlDoc.Tokens[tokPtr]; + tok = doc.Tokens[tokPtr]; } - if (HasFocus && selectionNotEmpty) { - RectangleD lineRect = new RectangleD (cb.X, lineHeight * y + cb.Top, pixX, lineHeight); + RectangleD lineRect = new RectangleD (cb.X, lineHeight * y + cb.Top, pixX, lineHeight); + if (selectionNotEmpty) { RectangleD selRect = lineRect; if (i >= selStart.Line && i <= selEnd.Line) { @@ -357,14 +413,29 @@ namespace Crow } } + //Draw line numbering + int curLine = i; + if (printLineNumbers){ + marginRect.Y = lineRect.Y; + + string strLN = (curLine+1).ToString (); + gr.SetSource (marginBG); + gr.Rectangle (marginRect); + gr.Fill(); + gr.SetSource (marginFG); + gr.MoveTo (marginRect.X + leftMarginGap + lineNumWidth - gr.TextExtents (strLN).Width, marginRect.Y + fe.Ascent); + gr.ShowText (strLN); + gr.Fill (); + } + if (!multilineToken) { - if (++tokPtr >= xmlDoc.Tokens.Length) + if (++tokPtr >= doc.Tokens.Length) break; - tok = xmlDoc.Tokens[tokPtr]; + tok = doc.Tokens[tokPtr]; } x = 0; - pixX = 0; + pixX = cb.Left; y++; @@ -383,7 +454,7 @@ namespace Crow } //gr.Translate (ScrollX, ScrollY); } finally { - xmlDoc.ExitReadLock (); + doc.ExitReadLock (); } } diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs index ada9c1b..26828d1 100644 --- a/CrowEditBase/src/TextDocument.cs +++ b/CrowEditBase/src/TextDocument.cs @@ -13,8 +13,8 @@ using static CrowEditBase.CrowEditBase; namespace CrowEditBase { public class TextDocument : Document { - public TextDocument (string fullPath) - : base (fullPath) { + public TextDocument (string fullPath, string editorPath = "default") + : base (fullPath, editorPath) { reloadFromFile (); } diff --git a/CrowEditBase/src/TreeNode.cs b/CrowEditBase/src/TreeNode.cs index 7de3c53..195216f 100644 --- a/CrowEditBase/src/TreeNode.cs +++ b/CrowEditBase/src/TreeNode.cs @@ -21,27 +21,41 @@ namespace CrowEditBase None, Compile, EmbeddedResource, - } + Project, + ProjectGroup, + } public abstract class TreeNode : CrowEditComponent { #region CTOR protected TreeNode () { } #endregion - ObservableList children = new ObservableList (); + ObservableList children = new ObservableList (); - protected bool isSelected, isExpanded; + protected bool isExpanded; public TreeNode Parent { get; private set; } public abstract string Caption { get; } public abstract NodeType NodeType { get; } - public T GetRoot () where T : TreeNode { + public abstract string Icon { get; } + public virtual string IconSub => null; + public T GetFirstAncestorOfType () where T : TreeNode { TreeNode n = this; - while (n.Parent != null) + while (n.Parent != null && !(n is T)) n = n.Parent; return (T)n; } + public virtual bool TryFindFileNode (string fullPath, out IFileNode node) { + foreach (IFileNode n in Flatten.OfType ()) { + if (n.FullPath == fullPath) { + node = n; + return true; + } + } + node = null; + return false; + } public ObservableList Childs { get => children; @@ -64,9 +78,11 @@ namespace CrowEditBase pn.Parent = null; children.Remove (pn); } - /*public override bool IsSelected { + public override bool IsSelected { get => base.IsSelected; set { + if (isSelected == value) + return; base.IsSelected = value; if (isSelected) { TreeNode pn = Parent; @@ -76,9 +92,9 @@ namespace CrowEditBase } } } - }*/ + } public virtual bool IsExpanded { - get { return isExpanded; } + get => isExpanded; set { if (value == isExpanded) return; @@ -88,8 +104,7 @@ namespace CrowEditBase } } public bool HasChildren => children?.Count > 0; - public abstract string Icon { get; } - public virtual string IconSub => null; + public IEnumerable Flatten { get { @@ -98,7 +113,6 @@ namespace CrowEditBase yield return node; } } - public virtual void SortChilds () { foreach (TreeNode pn in Childs) diff --git a/CrowEditBase/src/VirtualNode.cs b/CrowEditBase/src/VirtualNode.cs index 7da4991..593bf6f 100644 --- a/CrowEditBase/src/VirtualNode.cs +++ b/CrowEditBase/src/VirtualNode.cs @@ -25,7 +25,7 @@ namespace CrowEditBase public override CommandGroup Commands { get { - return null; + return null; } } } diff --git a/CrowEditBase/ui/CrowEdit.style b/CrowEditBase/ui/CrowEdit.style index 8bdd96d..f784b95 100644 --- a/CrowEditBase/ui/CrowEdit.style +++ b/CrowEditBase/ui/CrowEdit.style @@ -14,11 +14,18 @@ ControlIdle = "Jet"; DockWindowBackground = "DarkGrey"; +TreeItemBorderCornerRadius = "0"; +TreeItemBorderFG = "Transparent"; +TreeItemBorderHighlightFG = "DimGrey"; +TreeItemBackground = "Transparent"; +//TreeItemHighlight = " + Editor { Background="White"; Foreground="Black"; MouseWheelSpeed = "20"; - BubbleMouseEvent ="None"; + BubbleEvents ="None"; + ClipToClientRect = "false"; } icon { diff --git a/CrowEditBase/ui/TreeExpandable.template b/CrowEditBase/ui/TreeExpandable.template index 488f879..ea45ddb 100644 --- a/CrowEditBase/ui/TreeExpandable.template +++ b/CrowEditBase/ui/TreeExpandable.template @@ -1,5 +1,7 @@  - + @@ -7,20 +9,26 @@