From: Jean-Philippe Bruyère Date: Sat, 22 Feb 2025 08:17:04 +0000 (+0100) Subject: wip X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=80cf9a930f5b8604b47b5cf5a638101020321803;p=jp%2Fcrowedit.git wip --- diff --git a/.gitignore b/.gitignore index 69b037a..78de650 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +CrowDesignAssembly/ +netcoredbg/ #Autosave files *~ diff --git a/CrowEdit.csproj b/CrowEdit.csproj index 4132a1d..9415782 100644 --- a/CrowEdit.csproj +++ b/CrowEdit.csproj @@ -1,6 +1,6 @@  - net5 + net9 WinExe false @@ -18,15 +18,33 @@ false + false + runtime false + false + runtime false + false + runtime false + false + runtime + + false + false + runtime + + + false + false + runtime + diff --git a/CrowEdit.sln b/CrowEdit.sln index 7f96d42..db0e9fb 100644 --- a/CrowEdit.sln +++ b/CrowEdit.sln @@ -25,6 +25,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CEXmlPlugin", "plugins\CEXm EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow", "..\crow\Crow\Crow.csproj", "{5D8999F6-80D8-44CA-93F2-E18C9E44640C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CENugetPlugin", "plugins\CENugetPlugin\CENugetPlugin.csproj", "{B0E09E4D-1907-46D4-B136-C7378A558201}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drawing2D", "..\crow\Drawing2D\Drawing2D.csproj", "{22C1906B-BE08-4B31-AE02-2EE012CDA039}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CEEbnfPlugin", "plugins\CEEbnfPlugin\CEEbnfPlugin.csproj", "{CEE513F8-AE14-4588-8C9A-852455747A0B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,6 +65,18 @@ Global {5D8999F6-80D8-44CA-93F2-E18C9E44640C}.Debug|Any CPU.Build.0 = Debug|Any CPU {5D8999F6-80D8-44CA-93F2-E18C9E44640C}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D8999F6-80D8-44CA-93F2-E18C9E44640C}.Release|Any CPU.Build.0 = Release|Any CPU + {B0E09E4D-1907-46D4-B136-C7378A558201}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0E09E4D-1907-46D4-B136-C7378A558201}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0E09E4D-1907-46D4-B136-C7378A558201}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0E09E4D-1907-46D4-B136-C7378A558201}.Release|Any CPU.Build.0 = Release|Any CPU + {22C1906B-BE08-4B31-AE02-2EE012CDA039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22C1906B-BE08-4B31-AE02-2EE012CDA039}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22C1906B-BE08-4B31-AE02-2EE012CDA039}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22C1906B-BE08-4B31-AE02-2EE012CDA039}.Release|Any CPU.Build.0 = Release|Any CPU + {CEE513F8-AE14-4588-8C9A-852455747A0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CEE513F8-AE14-4588-8C9A-852455747A0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEE513F8-AE14-4588-8C9A-852455747A0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CEE513F8-AE14-4588-8C9A-852455747A0B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -68,6 +86,8 @@ Global {14E49365-6E22-4A27-B0E5-C6BBB347A85C} = {386C459C-4849-40C3-9D5A-4A8802A5A848} {93105D4F-3015-4C37-A377-9E4BD63DB582} = {386C459C-4849-40C3-9D5A-4A8802A5A848} {196D847E-D051-429B-892F-C405F036B8C2} = {386C459C-4849-40C3-9D5A-4A8802A5A848} + {B0E09E4D-1907-46D4-B136-C7378A558201} = {386C459C-4849-40C3-9D5A-4A8802A5A848} + {CEE513F8-AE14-4588-8C9A-852455747A0B} = {386C459C-4849-40C3-9D5A-4A8802A5A848} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {121E26CF-4A0F-4E74-BC0F-82BABEFDE8BF} diff --git a/CrowEditBase/CrowEditBase.csproj b/CrowEditBase/CrowEditBase.csproj index 0ba3dc6..569c244 100644 --- a/CrowEditBase/CrowEditBase.csproj +++ b/CrowEditBase/CrowEditBase.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net9 false @@ -15,7 +15,13 @@ - - + + + + + + <_Parameter1>0 + + diff --git a/CrowEditBase/icons/debug-bug.svg b/CrowEditBase/icons/debug-bug.svg new file mode 100644 index 0000000..8c73946 --- /dev/null +++ b/CrowEditBase/icons/debug-bug.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/debug-pause.svg b/CrowEditBase/icons/debug-pause.svg new file mode 100644 index 0000000..33f4c86 --- /dev/null +++ b/CrowEditBase/icons/debug-pause.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/debug-play.svg b/CrowEditBase/icons/debug-play.svg new file mode 100644 index 0000000..91ee85d --- /dev/null +++ b/CrowEditBase/icons/debug-play.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/debug-step-into.svg b/CrowEditBase/icons/debug-step-into.svg new file mode 100644 index 0000000..f60dbf4 --- /dev/null +++ b/CrowEditBase/icons/debug-step-into.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/CrowEditBase/icons/debug-step-out.svg b/CrowEditBase/icons/debug-step-out.svg new file mode 100644 index 0000000..8d052bf --- /dev/null +++ b/CrowEditBase/icons/debug-step-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/CrowEditBase/icons/debug-step-over.svg b/CrowEditBase/icons/debug-step-over.svg new file mode 100644 index 0000000..6deaf2a --- /dev/null +++ b/CrowEditBase/icons/debug-step-over.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/CrowEditBase/icons/debug-stop.svg b/CrowEditBase/icons/debug-stop.svg new file mode 100644 index 0000000..27d810a --- /dev/null +++ b/CrowEditBase/icons/debug-stop.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/file-cube.svg b/CrowEditBase/icons/file-cube.svg new file mode 100644 index 0000000..6df0f4a --- /dev/null +++ b/CrowEditBase/icons/file-cube.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 40b0773..6a28378 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -95,7 +95,14 @@ namespace CrowEditBase Tokenizer tokenizer = CreateTokenizer (); tokens = tokenizer.Tokenize (Source); SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); + + if (syntaxAnalyser == null) { + RootNode = null; + return; + } + syntaxAnalyser.Process (); + NotifyValueChanged("Exceptions", syntaxAnalyser.Exceptions); SyntaxNode newNode = syntaxAnalyser.Root.FindNodeIncludingSpan (TextSpan.FromStartAndLength (change.Start, change.ChangedText.Length)); @@ -137,6 +144,8 @@ namespace CrowEditBase currentTokenIndex = FindTokenIndexIncludingPosition (pos); CurrentNode = FindNodeIncludingSpan (currentToken.Span); NotifyValueChanged ("CurrentTokenString", (object)CurrentTokenString); + //NotifyValueChanged ("CurrentTokenType", (uint)(currentToken.Type)>>8); + NotifyValueChanged ("CurrentTokenType", (object)GetTokenTypeString(currentToken.Type)); }else { currentTokenIndex = -1; CurrentNode = null; @@ -153,6 +162,7 @@ namespace CrowEditBase return Colors.DarkSlateBlue; return Colors.Red; } + public virtual string GetTokenTypeString (TokenType tokenType) => tokenType.ToString(); protected abstract Tokenizer CreateTokenizer (); protected abstract SyntaxAnalyser CreateSyntaxAnalyser (); public abstract IList GetSuggestions (CharLocation loc); @@ -168,17 +178,20 @@ namespace CrowEditBase public abstract bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection); void parse () { Tokenizer tokenizer = CreateTokenizer (); - tokens = tokenizer.Tokenize (Source); - + tokens = tokenizer?.Tokenize (Source); SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); - //Stopwatch sw = Stopwatch.StartNew (); - syntaxAnalyser.Process (); - //sw.Stop(); - RootNode = syntaxAnalyser.Root; - - /*Console.WriteLine ($"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)"); + Stopwatch sw = Stopwatch.StartNew (); + syntaxAnalyser?.Process (); + sw.Stop(); + RootNode = syntaxAnalyser?.Root; + + //CrowEditBase.App.Log (LogType.Low, $"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)"); + if (syntaxAnalyser == null) + return; + LogItem log = CrowEditBase.App.GetLog(this.FileName); + log.ResetLog(); foreach (SyntaxException ex in syntaxAnalyser.Exceptions) - Console.WriteLine ($"{ex}");*/ + log.Add(LogType.Error, $"{ex}"); /*foreach (Token t in Tokens) Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}"); diff --git a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs index 371a8f3..dcac0eb 100644 --- a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs +++ b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs @@ -22,6 +22,7 @@ namespace CrowEditBase this.source = source; } public abstract void Process (); + protected Token curTok; protected SyntaxNode currentNode; protected int currentLine, tokIdx; @@ -37,5 +38,8 @@ namespace CrowEditBase } protected void setCurrentNodeEndLine (int endLine) => currentNode.EndLine = endLine; + + + //bool EOF => tokIdx == tokens.Length; } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs index a771f8d..4f10b43 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -44,7 +44,7 @@ namespace CrowEditBase public int StartLine { get; private set; } public virtual int LineCount => lineCount; public virtual bool IsComplete => LastTokenOffset.HasValue; - public virtual bool IsFoldable => Parent.StartLine != StartLine && lineCount > 1; + public virtual bool IsFoldable => IsComplete && Parent.StartLine != StartLine && lineCount > 1; public virtual SyntaxRootNode Root => Parent.Root; public virtual void UnfoldToTheTop () { isFolded = false; diff --git a/CrowEditBase/src/Compiler/Token.cs b/CrowEditBase/src/Compiler/Token.cs index a1a86d0..7f11721 100644 --- a/CrowEditBase/src/Compiler/Token.cs +++ b/CrowEditBase/src/Compiler/Token.cs @@ -16,7 +16,8 @@ namespace CrowEditBase public TextSpan Span => new TextSpan (Start, End); public string AsString (ReadOnlySpan source) => source.Slice (Start, Length).ToString(); - + public char GetChar (ReadOnlySpan source, int charIndex = 0) + => source[Start + charIndex]; public Token (TokenType type, int pos) { Type = type; Start = pos; diff --git a/CrowEditBase/src/Compiler/Tokenizer.cs b/CrowEditBase/src/Compiler/Tokenizer.cs index 80b9d24..b62d05c 100644 --- a/CrowEditBase/src/Compiler/Tokenizer.cs +++ b/CrowEditBase/src/Compiler/Tokenizer.cs @@ -41,7 +41,7 @@ namespace CrowEditBase } protected virtual void skipWhiteSpaces (ref SpanCharReader reader) { while(!reader.EndOfSpan) { - switch (reader.Peak) { + switch (reader.Peek) { case '\x85': case '\x2028': case '\xA': @@ -57,7 +57,7 @@ namespace CrowEditBase case '\x20': case '\x9': char c = reader.Read(); - while (reader.TryPeak (c)) + while (reader.TryPeek (c)) reader.Read(); addTok (ref reader, c == '\x20' ? TokenType.WhiteSpace : TokenType.Tabulation); break; diff --git a/CrowEditBase/src/CrowEditBase.cs b/CrowEditBase/src/CrowEditBase.cs index d18e5a5..fc80869 100644 --- a/CrowEditBase/src/CrowEditBase.cs +++ b/CrowEditBase/src/CrowEditBase.cs @@ -15,20 +15,78 @@ using Drawing2D; namespace CrowEditBase { public abstract class CrowEditBase : Interface { - protected Dictionary> FileAssociations = new Dictionary> (); - protected Dictionary> SupportedEditors = new Dictionary> (); - ObservableList logs = new ObservableList(); - public ObservableList MainLog => logs; - - public void Log(LogType type, string message) { - lock (logs) - logs.Add (new LogEntry(type, message)); + public static CrowEditBase App; + public CrowEditBase (int width, int height, bool singleThreaded = true) : base (width, height, singleThreaded) { + App = this; + MainLog = GetLog("CrowEdit"); + MainLog.IsOpened = true; + Log(LogType.Normal,"Crow edit started"); } - public void ResetLog () { - lock (logs) - logs.Clear (); + + #region logging + LogItem currentLog; + public LogItem CurrentLog { + get => currentLog; + set { + if (currentLog == value) + return; + + if (currentLog != null) + currentLog.IsSelected = false; + + currentLog = value; + NotifyValueChanged (currentLog); + + if (currentLog == null) + return; + + currentLog.IsSelected = true; + } + } + public ObservableList Logs = new ObservableList(); + public ObservableList OpenedLogs = new ObservableList(); + internal LogItem MainLog; + [Obsolete]public void Log(LogType type, string message) { + MainLog.Add (type, message); + } + [Obsolete]public void ResetLog () { + MainLog.ResetLog(); + } + public LogItem GetLog(string name) { + LogItem li = Logs.FirstOrDefault(l=>string.Equals(l.Name,name,StringComparison.OrdinalIgnoreCase)); + if (li == null) { + li = new LogItem(name); + lock (Logs) { + lock(UpdateMutex) + Logs.Add(li); + } + } + return li; + } + internal void OpenLog(LogItem li) { + lock(OpenedLogs) + OpenedLogs.Add(li); + } + internal void CloseLog(LogItem li) { + lock(OpenedLogs) { + if (li.IsSelected) { + int idx = OpenedLogs.IndexOf(li); + OpenedLogs.RemoveAt(idx); + int count = OpenedLogs.Count(); + if (idx < count) + OpenedLogs[idx].IsSelected = true; + else if (count > 0) + OpenedLogs[count-1].IsSelected = true; + } else + OpenedLogs.Remove(li); + } } + #endregion + + #region File associations and supported editors + protected Dictionary> FileAssociations = new Dictionary> (); + protected Dictionary> SupportedEditors = new Dictionary> (); public void AddFileAssociation (string extension, Type clientClass) { if (!FileAssociations.ContainsKey (extension)) FileAssociations.Add (extension, new List ()); @@ -55,14 +113,9 @@ namespace CrowEditBase editorPath = SupportedEditors.ContainsKey (clientType) ? SupportedEditors[clientType].FirstOrDefault () : null; return editorPath != null; } + #endregion - - public static CrowEditBase App; - public CrowEditBase (int width, int height) : base (width, height, true) { - App = this; - } - protected const string _defaultFileName = "unnamed.txt"; Document currentDocument; @@ -179,13 +232,13 @@ namespace CrowEditBase NotifyValueChanged (CurrentDir); } } - public string PluginsDirecory { - get => Configuration.Global.Get("PluginsDirecory", defaultPluginsDirectory); + public string PluginsDirectory { + get => Configuration.Global.Get("PluginsDirectory", defaultPluginsDirectory); set { - if (PluginsDirecory == value) + if (PluginsDirectory == value) return; - Configuration.Global.Set ("PluginsDirecory", value); - NotifyValueChanged (PluginsDirecory); + Configuration.Global.Set ("PluginsDirectory", value); + NotifyValueChanged (PluginsDirectory); } } public string CurrentFilePath { @@ -270,7 +323,7 @@ namespace CrowEditBase g.DataSource = dataSource; return g as Window; } catch (Exception ex) { - Console.WriteLine (ex.ToString ()); + Log (LogType.Error, ex.ToString ()); } return null; } @@ -287,72 +340,84 @@ namespace CrowEditBase public ActionCommand CMDOptions_SelectPluginsDirectory => new ActionCommand ("...", () => { FileDialog dlg = App.LoadIMLFragment (@" - "); - dlg.OkClicked += (sender, e) => PluginsDirecory = (sender as FileDialog).SelectedFileFullPath; + dlg.OkClicked += (sender, e) => PluginsDirectory = (sender as FileDialog).SelectedFileFullPath; dlg.DataSource = this; } ); public ActionCommand CMDOptions_ResetPluginsDirectory => new ActionCommand ("Reset", () => { - PluginsDirecory = defaultPluginsDirectory; + PluginsDirectory = defaultPluginsDirectory; } ); static string defaultPluginsDirectory => Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".config", "CrowEdit", "plugins"); protected void loadPlugins () { - if (!Directory.Exists (PluginsDirecory)) - return; + Log(LogType.Message, $"Searching for plugins in {PluginsDirectory}"); - foreach (string pluginDir in Directory.GetDirectories (PluginsDirecory)) { + if (!Directory.Exists (PluginsDirectory)) { + Log(LogType.Error, $"Plugins directory not found: {PluginsDirectory}"); + return; + } + + List pluginsToReload = new List(); + foreach (string pluginDir in Directory.GetDirectories (PluginsDirectory)) { Plugin plugin = new Plugin (pluginDir); Plugins.Add (plugin); - plugin.Load (); + if (!plugin.Load ()) + pluginsToReload.Add(plugin); + } + foreach (Plugin p in pluginsToReload) { + p.Unload(); + if (!p.Load()) + App.Log(LogType.Warning, $"Plugin load failed: {p.Name}"); + } + } public IEnumerable AllLoadContexts => System.Runtime.Loader.AssemblyLoadContext.All; - #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 (); + #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 + string defaultEditorITemps = @" + + + + + + + + + + + + + + + "; + #endregion + + #region main options public int CrowUpdateInterval { get => Crow.Interface.UPDATE_INTERVAL; set { @@ -441,6 +506,6 @@ namespace CrowEditBase } } -#endregion + #endregion } } \ No newline at end of file diff --git a/CrowEditBase/src/Debug/Debugger.cs b/CrowEditBase/src/Debug/Debugger.cs index 3fe227a..221c5d8 100644 --- a/CrowEditBase/src/Debug/Debugger.cs +++ b/CrowEditBase/src/Debug/Debugger.cs @@ -19,6 +19,8 @@ namespace CrowEditBase Ready, /// running state received Running, + /// abort requested + Stopping, /// stopped event received Stopped, } @@ -28,12 +30,12 @@ namespace CrowEditBase public virtual CommandGroup Commands => new CommandGroup ( CMDDebugStart, CMDDebugPause, CMDDebugStop, CMDDebugStepIn, CMDDebugStepOver, CMDDebugStepOut); protected virtual void initCommands () { - CMDDebugStart = new ActionCommand ("Start", Start, "#Icons.debug-play.svg"); - CMDDebugPause = new ActionCommand ("Pause", Pause, "#Icons.debug-pause.svg", false); - CMDDebugStop = new ActionCommand ("Stop", Stop, "#Icons.debug-stop.svg", false); - CMDDebugStepIn = new ActionCommand ("Step in", StepIn, "#Icons.debug-step-into.svg", false); - CMDDebugStepOut = new ActionCommand ("Step out", StepOut, "#Icons.debug-step-out.svg", false); - CMDDebugStepOver = new ActionCommand ("Step over", StepOver, "#Icons.debug-step-over.svg", false); + CMDDebugStart = new ActionCommand ("Start", Start, "#icons.debug-play.svg"); + CMDDebugPause = new ActionCommand ("Pause", Pause, "#icons.debug-pause.svg", false); + CMDDebugStop = new ActionCommand ("Stop", Stop, "#icons.debug-stop.svg", false); + CMDDebugStepIn = new ActionCommand ("Step in", StepIn, "#icons.debug-step-into.svg", false); + CMDDebugStepOut = new ActionCommand ("Step out", StepOut, "#icons.debug-step-out.svg", false); + CMDDebugStepOver = new ActionCommand ("Step over", StepOver, "#icons.debug-step-over.svg", false); } @@ -47,12 +49,16 @@ namespace CrowEditBase { if (currentState == value) return; + currentState = value; CMDDebugStepIn.CanExecute = CMDDebugStepOut.CanExecute = CMDDebugStepOver.CanExecute = (CurrentState == Status.Stopped); CMDDebugStart.CanExecute = (CurrentState == Status.Ready || CurrentState == Status.Stopped); - CMDDebugPause.CanExecute = CMDDebugStop.CanExecute = (CurrentState == Status.Running); + CMDDebugPause.CanExecute = (CurrentState == Status.Running); + CMDDebugStop.CanExecute = (CurrentState == Status.Running || CurrentState == Status.Stopped); + + NotifyValueChanged(currentState); } } StackFrame executingFile; @@ -80,6 +86,7 @@ namespace CrowEditBase return; currentThread = value; NotifyValueChanged(currentThread); + onCurrentThreadChanged(); } } public StackFrame CurrentFrame diff --git a/CrowEditBase/src/Document.cs b/CrowEditBase/src/Document.cs index 5aa101c..56ef878 100644 --- a/CrowEditBase/src/Document.cs +++ b/CrowEditBase/src/Document.cs @@ -63,9 +63,11 @@ namespace CrowEditBase public void OnQueryClose (object sender, EventArgs e){ CloseEvent.Raise (this, null); } + protected abstract void saveFileDialog_OkClicked (object sender, EventArgs e); public void SaveAs () { App.LoadIMLFragment ( - "" + @"" ).DataSource = this; } public void Save () { diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs index f00adfb..a9bbb77 100644 --- a/CrowEditBase/src/Editor.cs +++ b/CrowEditBase/src/Editor.cs @@ -338,8 +338,7 @@ namespace Crow selStart = selectionStart.Value; selEnd = CurrentLoc.Value; } - } else - IFace.forceTextCursor = true; + } //} if (document.Lenght > 0) { @@ -356,7 +355,7 @@ namespace Crow if (l.Length > 0) { int size = l.Length * 4 + 1; if (bytes.Length < size) - bytes = size > 512 ? new byte[size] : stackalloc byte[size]; + bytes = new byte[size]; encodedBytes = document.GetText (l).ToUtf8 (bytes); bytes[encodedBytes++] = 0; @@ -477,8 +476,7 @@ namespace Crow } //} - Rectangle c = ScreenCoordinates (textCursor.Value + Slot.Position + ClientRectangle.Position); - ctx.ResetClip (); + Rectangle c = ContextCoordinates (textCursor.Value + Slot.Position + ClientRectangle.Position); Foreground.SetAsSource (IFace, ctx, c); ctx.LineWidth = 1.0; ctx.MoveTo (0.5 + c.X, c.Y); @@ -576,10 +574,6 @@ namespace Crow DbgLogger.EndEvent(DbgEvtType.GOMeasure); } } - public override void Paint (IContext ctx) { - base.Paint (ctx); - IFace.forceTextCursor = true; - } protected override void onDraw (IContext gr) { //base.onDraw (gr); @@ -602,6 +596,14 @@ namespace Crow if (ClipToClientRect) gr.Restore (); } + public override bool Paint(IContext ctx) + { + bool painted = base.Paint(ctx); + if (HasFocus && painted && IFace.drawTextCursor) { + DrawCursor(ctx, out Rectangle r); + } + return painted; + } #endregion #region Mouse handling @@ -638,7 +640,7 @@ namespace Crow if (HasFocus && IFace.IsDown (MouseButton.Left)) { CurrentLoc = hoverLoc; autoAdjustScroll = true; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); RegisterForRedraw (); } } @@ -652,7 +654,7 @@ namespace Crow else if (!selectionStart.HasValue) selectionStart = CurrentLoc; CurrentLoc = hoverLoc; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); RegisterForRedraw (); e.Handled = true; } @@ -808,7 +810,7 @@ namespace Crow return; } autoAdjustScroll = true; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); e.Handled = true; /*} finally { document.ExitReadLock (); @@ -890,7 +892,7 @@ namespace Crow CurrentLoc = document.GetLocation (change.Start + change.ChangedText.Length); textMeasureIsUpToDate = false; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); autoAdjustScroll = true; RegisterForGraphicUpdate (); diff --git a/CrowEditBase/src/LogViewerWidget.cs b/CrowEditBase/src/LogViewerWidget.cs index c63d67e..835d2f4 100644 --- a/CrowEditBase/src/LogViewerWidget.cs +++ b/CrowEditBase/src/LogViewerWidget.cs @@ -9,6 +9,9 @@ using System.Collections; using Drawing2D; using System.Threading.Tasks; using System.Linq; +using System.Collections.Generic; +using CrowEditBase; +using System.Diagnostics; namespace Crow { @@ -30,8 +33,13 @@ namespace Crow Custom1 = 0x0040, Custom2 = 0x0080, Custom3 = 0x0100, + code = 0x1000, + crowEdit = 0x2000, + Plugin = 0x4000, + + Custom = Custom1 | Custom2 | Custom3, - all = Message | WarnErr | Custom | Debug, + all = 0xffff } public class LogEntry { public LogType Type; @@ -44,9 +52,10 @@ namespace Crow } public class LogViewerWidget : ScrollingObject { - ObservableList lines; - LogEntry[] filteredLines; + LogItem logger; + IEnumerable filteredLines; object filteredLinesMutex = new object (); + bool updateFilteredLinesRequest = true; bool scrollOnOutput, caseSensitiveSearch, allWordSearch; int visibleLines = 1; FontExtents fe; @@ -69,6 +78,32 @@ namespace Crow NotifyValueChanged ("ScrollOnOutput", scrollOnOutput); } } + public LogItem Logger { + get => logger; + set { + if (logger == value) + return; + if (logger != null) { + lock(logger.LogMutext) { + logger.log.ListAdd -= Lines_ListAdd; + logger.log.ListRemove -= Lines_ListRemove; + logger.log.ListClear -= Lines_ListClear; + } + } + logger = value; + if (logger != null) { + lock(logger.LogMutext) { + logger.log.ListAdd += Lines_ListAdd; + logger.log.ListRemove += Lines_ListRemove; + logger.log.ListClear += Lines_ListClear; + } + updateFilteredLinesRequest = true; + } + NotifyValueChanged("Logger", logger); + if (IsVisible) + RegisterForRedraw(); + } + } [DefaultValue(LogType.all)] public LogType Filter { get => filter; @@ -81,39 +116,6 @@ namespace Crow RegisterForRedraw (); } } - bool updateFilteredLinesRequest = true; - void updateFilteredLines () { - if (Lines != null) { - lock (filteredLinesMutex) - lock (lines) - filteredLines = Lines.Where (l=>((int)l.Type & (int)filter) > 0).ToArray(); - MaxScrollY = filteredLines.Length - visibleLines; - if (scrollOnOutput) - ScrollY = MaxScrollY; - } - updateFilteredLinesRequest = false; - } - public virtual ObservableList Lines { - get => lines; - set { - if (lines == value) - return; - if (lines != null) { - lines.ListAdd -= Lines_ListAdd; - lines.ListRemove -= Lines_ListRemove; - lines.ListClear -= Lines_ListClear; - } - lines = value; - if (lines != null) { - lines.ListAdd += Lines_ListAdd; - lines.ListRemove += Lines_ListRemove; - lines.ListClear += Lines_ListClear; - updateFilteredLinesRequest = true; - } - NotifyValueChanged ("Lines", lines); - RegisterForGraphicUpdate (); - } - } public int CurrentEntryIndex { get => curEntryIdx; set { @@ -128,6 +130,36 @@ namespace Crow RegisterForRedraw(); } } + + void updateFilteredLines () { + if (logger != null) { + lock (filteredLinesMutex) { + //Console.WriteLine("updatefilteredlines"); + lock (logger.LogMutext) + filteredLines = logger.log.Where (l=>((int)l.Type & (int)filter) > 0); + MaxScrollY = filteredLines == null ? 0 : filteredLines.Count() - visibleLines; + NotifyValueChanged ("ChildHeightRatio", Math.Min (1.0, (double)visibleLines / filteredLines.Count())); + } + if (scrollOnOutput) + ScrollY = MaxScrollY; + } + updateFilteredLinesRequest = false; + } + void updateHoverEntryIdx (Point mpos) { + PointD mouseLocalPos = ScreenPointToLocal (mpos); + lock (filteredLinesMutex) { + if (filteredLines == null) { + hoverEntryIdx = -1; + return; + } + hoverEntryIdx = ScrollY + (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height)), filteredLines.Count() - 1); + } + RegisterForRedraw (); + } + + + + #region Searching [DefaultValue (true)] public virtual bool CaseSensitiveSearch { get { return caseSensitiveSearch; } @@ -207,7 +239,9 @@ namespace Crow void performSearch (string str, bool next = false, bool backward = false) { if (string.IsNullOrEmpty (str) || filteredLines == null) return; - LogEntry[] entries = filteredLines.ToArray (); + LogEntry[] entries; + lock(filteredLinesMutex) + entries = filteredLines.ToArray (); if (entries.Length == 0) { CurrentEntryIndex = -1; return; @@ -217,27 +251,16 @@ namespace Crow else performSearchForward (entries, str, next); } - + #endregion void Lines_ListAdd (object sender, ListChangedEventArg e) { updateFilteredLinesRequest = true; - RegisterForRedraw(); - // try - // { - //updateFilteredLines(); - - // } - // catch (System.Exception ex) - // { - // Console.WriteLine ($"list add valueChange handler bug:{ex}"); - // } + if (IsVisible) + RegisterForRedraw(); } - void Lines_ListRemove (object sender, ListChangedEventArg e) { - /*updateFilteredLines(); - MaxScrollY = filteredLines.Length - visibleLines;*/ updateFilteredLinesRequest = true; RegisterForRedraw (); } @@ -248,7 +271,9 @@ namespace Crow RegisterForRedraw (); } + public Stopwatch perf = new Stopwatch (); + #region widget overrides public override void OnLayoutChanges (LayoutingType layoutType) { base.OnLayoutChanges (layoutType); @@ -259,10 +284,18 @@ namespace Crow gr.SetFontSize (Font.Size); fe = gr.FontExtents; } + + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height); + if (updateFilteredLinesRequest) updateFilteredLines (); - visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height); - MaxScrollY = filteredLines == null ? 0 : filteredLines.Length - visibleLines; + else { + int count = 0; + lock (filteredLinesMutex) + count = filteredLines.Count(); + MaxScrollY = filteredLines == null ? 0 : count - visibleLines; + NotifyValueChanged ("ChildHeightRatio", Math.Min (1.0, (double)visibleLines / count)); + } } } protected override void onDraw (IContext gr) @@ -280,27 +313,30 @@ namespace Crow Rectangle r = ClientRectangle; - double y = ClientRectangle.Y; double x = ClientRectangle.X - ScrollX; + IEnumerable entries; lock (filteredLinesMutex) { - for (int i = 0; i < visibleLines; i++) { - int idx = i + ScrollY; - if (idx >= filteredLines.Length) - break; - LogEntry le = filteredLines[idx]; + entries = filteredLines.Skip(ScrollY).Take(visibleLines); + } - if (idx == curEntryIdx) { - gr.Rectangle (x, y, r.Width, fe.Height); - gr.SetSource (Color.Parse ("#5555ff55")); - gr.Fill (); - } else if (idx == hoverEntryIdx) { - gr.Rectangle (x, y, r.Width, fe.Height); - gr.SetSource (Color.Parse ("#8B451355")); - gr.Fill (); - } + //perf.Restart(); + + for (int i = 0; i < entries.Count(); i++) { + int idx = i + ScrollY; + LogEntry le = entries.ElementAt(i); + if (idx == curEntryIdx) { + gr.Rectangle (x, y, r.Width, fe.Height); + gr.SetSource (Color.Parse ("#5555ff55")); + gr.Fill (); + } else if (idx == hoverEntryIdx) { + gr.Rectangle (x, y, r.Width, fe.Height); + gr.SetSource (Color.Parse ("#8B451355")); + gr.Fill (); + } + if (!string.IsNullOrEmpty(le.msg)) { switch (le.Type) { case LogType.Low: gr.SetSource (Colors.DimGrey); @@ -329,13 +365,21 @@ namespace Crow case LogType.Custom3: gr.SetSource (Colors.LightPink); break; + default: + gr.SetSource (Colors.Grey); + break; } gr.MoveTo (x, y + fe.Ascent); - gr.ShowText (le.msg); - y += fe.Height; + ReadOnlySpan tmp = le.msg.AsSpan(0, Math.Min (400, le.msg.Length)); + gr.ShowText (tmp); } + y += fe.Height; } + /*perf.Stop(); + Console.WriteLine($"log onDraw: {visibleLines} lines in {perf.ElapsedMilliseconds} ms");*/ + } + public override void onMouseLeave(object sender, MouseMoveEventArgs e) { hoverEntryIdx = -1; @@ -360,17 +404,18 @@ namespace Crow base.onMouseWheel(sender, e); updateHoverEntryIdx (IFace.MousePosition); } - void updateHoverEntryIdx (Point mpos) { - PointD mouseLocalPos = ScreenPointToLocal (mpos); - lock (filteredLinesMutex) { - if (filteredLines == null) { - hoverEntryIdx = -1; - return; + protected override void Dispose(bool disposing) + { + if (logger != null) { + lock(logger.LogMutext) { + logger.log.ListAdd -= Lines_ListAdd; + logger.log.ListRemove -= Lines_ListRemove; + logger.log.ListClear -= Lines_ListClear; } - hoverEntryIdx = ScrollY + (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height)), filteredLines.Length - 1); - } - RegisterForRedraw (); - } - } + } + base.Dispose(disposing); + } + #endregion + } } diff --git a/CrowEditBase/src/Logger.cs b/CrowEditBase/src/Logger.cs new file mode 100644 index 0000000..1b8cc73 --- /dev/null +++ b/CrowEditBase/src/Logger.cs @@ -0,0 +1,78 @@ +// Copyright (c) 2021-2025 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections.Generic; +using System.Linq; +using Crow; +using CrowEditBase; +using Drawing2D; +using static CrowEditBase.CrowEditBase; + +namespace CrowEditBase +{ + public class LogItem : CrowEditComponent { + int selectedIndex = -1; + bool isOpened = false; + public Command CMDReset, CMDCopy; + public CommandGroup Commands => new CommandGroup (CMDCopy, CMDReset); + + public int SelectedIndex { + get => selectedIndex; + set { + if (selectedIndex == value) + return; + selectedIndex = value; + NotifyValueChanged(selectedIndex); + CMDCopy.CanExecute = !(value < 0); + } + } + public bool IsOpened { + get => isOpened; + set { + if (isOpened == value) + return; + isOpened = value; + NotifyValueChanged(isOpened); + lock (LogMutext) { + if (isOpened) { + App.OpenLog(this); + } else + App.CloseLog(this); + } + IsSelected = value; + } + } + + public string Name; + public ObservableList log; + + public object LogMutext = new object(); + public LogItem(string name) { + Name = name; + log = new ObservableList(); + CMDReset = new ActionCommand ("Clear", ResetLog); + CMDCopy = new ActionCommand ("Copy message", CopyMessage); + } + + + public void Add(LogType type, string message) { + lock (LogMutext) + log.Add (new LogEntry(type, message)); + } + public void ResetLog () { + lock (LogMutext) + log.Clear (); + } + public void CopyMessage () { + lock (LogMutext) { + if (selectedIndex < log.Count && selectedIndex >= 0) + App.Clipboard = log.ElementAt(selectedIndex).msg; + } + } + public void OnQueryClose (object sender, EventArgs e){ + IsOpened = false; + } + } + +} diff --git a/CrowEditBase/src/Plugin.cs b/CrowEditBase/src/Plugin.cs index aa67fc3..be21bf9 100644 --- a/CrowEditBase/src/Plugin.cs +++ b/CrowEditBase/src/Plugin.cs @@ -27,6 +27,7 @@ namespace CrowEditBase assembly = loadContext.Assemblies.FirstOrDefault (a=>a.GetName().Name == assemblyName.Name); return assembly != null; } + public Drawing2D.Color ColorStatus => isLoaded ? Drawing2D.Colors.Green : Drawing2D.Colors.Red; public virtual bool IsLoaded { get { return isLoaded; } set { @@ -36,6 +37,8 @@ namespace CrowEditBase isLoaded = value; NotifyValueChanged (isLoaded); + NotifyValueChanged ("ColorStatus", ColorStatus); + CMDLoad.CanExecute = !IsLoaded; CMDReload.CanExecute = CMDUnload.CanExecute = IsLoaded; @@ -52,14 +55,15 @@ namespace CrowEditBase CMDLoad, CMDUnload, CMDReload); protected virtual void initCommands () { - CMDLoad = new ActionCommand ("Load", Load, "#icons.reply.svg", false); + CMDLoad = new ActionCommand ("Load", () => { Load(); }, "#icons.reply.svg", false); CMDUnload = new ActionCommand ("Unload", Unload, "#icons.share-arrow.svg", false); CMDReload = new ActionCommand ("Reload", () => { Unload(); Load();}, "#icons.refresh.svg", false); } - public void Load () { + // return false on type load exception so the plugin load will be retried 1 time + public bool Load () { if (isLoaded) - return; + return true; if (loadContext == null) loadContext = new PluginsLoadContext(FullPath); @@ -81,6 +85,10 @@ namespace CrowEditBase foreach (string associations in fileAssociations.Split (';')) { string[] typeExts = associations.Split (':'); Type clientClass = loadContext.MainAssembly.GetType (typeExts[0].Trim()); + if (clientClass == null) { + App.Log(LogType.Plugin | LogType.Warning | LogType.Debug, $"Plugin first load failed: {Name}"); + return false; + } foreach (string ext in typeExts[1].Split (','))//supported extension comma separated list App.AddFileAssociation (ext.Trim(), clientClass); if (typeExts.Length < 3) @@ -90,12 +98,15 @@ namespace CrowEditBase } } catch (System.Exception ex) { - Console.WriteLine ($"[Plugin]Error reading 'default.conf' for {FullPath}: {ex.Message}"); + App.Log (LogType.Error|LogType.Plugin, $"Error reading 'default.conf' for {FullPath}: {ex.Message}"); + return true; } } } IsLoaded = true; + App.Log(LogType.Normal, $"Plugin loaded: {Name}"); + return true; } public void Unload () { if (!isLoaded) @@ -105,7 +116,7 @@ namespace CrowEditBase App.GetService (serviceClass)?.Stop(); App.RemoveCrowAssembly (loadContext.MainAssembly); - + App.Log(LogType.Plugin | LogType.Normal, $"Plugin unloaded: {Name}"); IsLoaded = false; } } diff --git a/CrowEditBase/src/PluginsLoadContext.cs b/CrowEditBase/src/PluginsLoadContext.cs index be4fd16..030b882 100644 --- a/CrowEditBase/src/PluginsLoadContext.cs +++ b/CrowEditBase/src/PluginsLoadContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// Copyright (c) 2021-2025 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -37,8 +37,8 @@ namespace CrowEditBase } protected override Assembly Load(AssemblyName assemblyName) { string assemblyPath = Path.Combine (fullPath, assemblyName.Name + ".dll"); + //App.Log (LogType.Message, $"[PluginsLoadContext:{Name}] Trying: {assemblyName.ToString()} => {assemblyPath}"); return File.Exists (assemblyPath) ? LoadFromAssemblyPath (assemblyPath) : null; } - } } diff --git a/CrowEditBase/src/Service.cs b/CrowEditBase/src/Service.cs index fa33fd1..7d8f1cc 100644 --- a/CrowEditBase/src/Service.cs +++ b/CrowEditBase/src/Service.cs @@ -18,16 +18,19 @@ namespace CrowEditBase Paused, Stopped } + LogItem log; + protected void Log(LogType type, string message) => log.Add(type, message); protected Service () { + Name = this.GetType().Name; + log = CrowEditBase.App.MainLog; CMDStart = new ActionCommand ("Start", Start, "#icons.play-button.svg", true); CMDStop = new ActionCommand ("Stop", Stop, "#icons.stop.svg", false); CMDPause = new ActionCommand ("Pause", Pause, "#icons.pause-symbol.svg", false); CMDOpenConfig = new ActionCommand ("Service configuration", () => CrowEditBase.App.LoadWindow (ConfigurationWindowPath, this), "#icons.cogwheel.svg", true); Commands = new CommandGroup (CMDStart, CMDPause, CMDStop, CMDOpenConfig); - - if (CrowEditBase.App.TryGetWindow (ConfigurationWindowPath, out Window win)) - win.DataSource = this; + ensureConfigWinDataSource(); + Log(LogType.Low, $"[{Name}] Service Instanciated"); } public Command CMDStart, CMDStop, CMDPause, CMDOpenConfig; public CommandGroup Commands; @@ -50,8 +53,13 @@ namespace CrowEditBase CMDStart.CanExecute = !IsRunning; CMDPause.CanExecute = IsRunning; CMDStop.CanExecute = IsRunning || CurrentState == Status.Paused; + Log(LogType.High, $"[{Name}] Status: {previousState} -> {newState}"); } - + protected void ensureConfigWinDataSource() { + if (CrowEditBase.App.TryGetWindow (ConfigurationWindowPath, out Window win)) + win.DataSource = this; + } + public readonly string Name; public abstract void Start (); public abstract void Stop (); public abstract void Pause (); diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs index 94b200b..24319dc 100644 --- a/CrowEditBase/src/SourceEditor.cs +++ b/CrowEditBase/src/SourceEditor.cs @@ -241,7 +241,7 @@ namespace Crow if (HasFocus && IFace.IsDown (MouseButton.Left)) { CurrentLoc = hoverLoc; autoAdjustScroll = true; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); RegisterForRedraw (); } } @@ -345,10 +345,10 @@ namespace Crow case Key.Enter: case Key.KeypadEnter: //doc.updateCurrentTokAndNode (Selection.Start); - Console.WriteLine ($"*** Current Token: {doc.CurrentToken} Current Node: {doc.CurrentNode}"); + //Console.WriteLine ($"*** Current Token: {doc.CurrentToken} Current Node: {doc.CurrentNode}"); update (new TextChange (selection.Start, selection.Length, Document.GetLineBreak ())); autoAdjustScroll = true; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); e.Handled = true; return; } @@ -590,8 +590,7 @@ namespace Crow selStart = selectionStart.Value; selEnd = CurrentLoc.Value; } - } else - IFace.forceTextCursor = true; + } double spacePixelWidth = gr.TextExtents (" ").XAdvance; @@ -643,7 +642,7 @@ namespace Crow int size = buff.Length * 4 + 1; if (bytes.Length < size) - bytes = size > 512 ? new byte[size] : stackalloc byte[size]; + bytes = new byte[size]; int encodedBytes = buff.ToUtf8 (bytes); @@ -663,7 +662,7 @@ namespace Crow RectangleD lineRect = new RectangleD (cb.X, pixY, pixX - cb.X, lineHeight); if (CurrentNode != null && l >= nodeStart.Value.Line && l <= nodeEnd.Value.Line) - fillHighlight (gr, l, nodeStart.Value, nodeEnd.Value, lineRect, new Color(0.0,0.1,0.0,0.1));; + fillHighlight (gr, l, nodeStart.Value, nodeEnd.Value, lineRect, new Color(0.0,0.1,0.0,0.08));; #if DEBUG_NODES if (doc.EditedNode != null && l >= editNodeStart.Value.Line && l <= editNodeEnd.Value.Line) fillHighlight (gr, l, editNodeStart.Value, editNodeEnd.Value, lineRect, new Color(0,0.5,0,0.2));; @@ -796,5 +795,6 @@ namespace Crow } //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}"); } + } } \ No newline at end of file diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs index dfc3146..84cfb3a 100644 --- a/CrowEditBase/src/TextDocument.cs +++ b/CrowEditBase/src/TextDocument.cs @@ -89,6 +89,7 @@ namespace CrowEditBase } origSource = source; NotifyValueChanged ("IsDirty", IsDirty); + CMDSave.CanExecute = IsDirty; } protected override void readFromDisk() { @@ -120,7 +121,7 @@ namespace CrowEditBase - protected void saveFileDialog_OkClicked (object sender, EventArgs e) + protected override void saveFileDialog_OkClicked (object sender, EventArgs e) { FileDialog fd = sender as FileDialog; @@ -180,6 +181,7 @@ namespace CrowEditBase } protected bool disableTextChangedEvent = false; protected virtual void apply (TextChange change) { + Span tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)]; ReadOnlySpan src = source.AsSpan (); src.Slice (0, change.Start).CopyTo (tmp); @@ -189,6 +191,9 @@ namespace CrowEditBase source = tmp.ToString (); lines.Update (change); + + NotifyValueChanged ("IsDirty", IsDirty); + CMDSave.CanExecute = IsDirty; } protected void applyTextChange (TextChange change, object triggeringEditor = null) { editorRWLock.EnterWriteLock (); diff --git a/CrowEditBase/ui/CrowEdit.style b/CrowEditBase/ui/CrowEdit.style index 70f574f..3895519 100644 --- a/CrowEditBase/ui/CrowEdit.style +++ b/CrowEditBase/ui/CrowEdit.style @@ -1,10 +1,10 @@ -SmallUIFont = "sans, 10"; -SmallFont = "consolas, 10"; +SmallUIFont = "sans, 12"; +SmallFont = "consolas, 12"; InactiveTabBackground = "DarkGrey"; SelectedTabBackground = "Onyx"; InactiveTabForeground = "Grey"; SelectedTabForeground = "White"; -MenuIconSize = "16"; +MenuIconSize = "22"; ControlForeground = "LightGrey"; ControlCaptionHoverColor = "White"; @@ -20,6 +20,11 @@ TreeItemBorderHighlightFG = "DimGrey"; TreeItemBackground = "Transparent"; //TreeItemHighlight = " +TxtInFileDialog { + Margin = "2"; + Font = "mono, 14"; +} + Editor { Focusable="true"; Height="Stretched"; @@ -54,9 +59,8 @@ MemberViewHStack { IcoBut { Template = "#ui.IcoBut.template"; MinimumSize = "10,10"; - Width = "8"; - Height = "14"; - Background = "White"; + Width = "32"; + Height = "32"; } Spinner { Template = "#ui.spinner.template"; @@ -133,7 +137,7 @@ CheckBox { Template= "#Crow.CheckBox2.template"; Width = "Stretched"; Height = "Fit"; - CornerRadius = "3"; + CornerRadius = "0"; Background = "${ControlIdle}"; Foreground = "${ControlForeground}"; Checked = "{Background=${ControlHighlight}}"; @@ -143,4 +147,5 @@ CheckBox { } LogViewerWidget { Background = "0.01,0.01,0.01,1"; -} \ No newline at end of file + MouseWheelSpeed = "2"; +} diff --git a/CrowEditBase/ui/DockWinTitleBarMenu.itemp b/CrowEditBase/ui/DockWinTitleBarMenu.itemp index 9ecd788..c62e171 100644 --- a/CrowEditBase/ui/DockWinTitleBarMenu.itemp +++ b/CrowEditBase/ui/DockWinTitleBarMenu.itemp @@ -3,7 +3,7 @@ diff --git a/CrowEditBase/ui/DockWindow.template b/CrowEditBase/ui/DockWindow.template index 3540140..befd6e0 100644 --- a/CrowEditBase/ui/DockWindow.template +++ b/CrowEditBase/ui/DockWindow.template @@ -3,12 +3,12 @@ - + - diff --git a/CrowEditBase/ui/IcoBut.template b/CrowEditBase/ui/IcoBut.template index 325381a..8cd0f1f 100644 --- a/CrowEditBase/ui/IcoBut.template +++ b/CrowEditBase/ui/IcoBut.template @@ -3,5 +3,5 @@ BorderWidth="1" BorderStyle="Normal" MouseDown="{BorderStyle=Sunken}" MouseUp="{BorderStyle=Normal}"> - - + + \ No newline at end of file diff --git a/CrowEditBase/ui/MenuButton.template b/CrowEditBase/ui/MenuButton.template index 6a9f05d..d220f1a 100644 --- a/CrowEditBase/ui/MenuButton.template +++ b/CrowEditBase/ui/MenuButton.template @@ -5,7 +5,7 @@ MouseEnter="{Background=${ControlHighlight}}" MouseLeave="{Background=Transparent}"> -