aboutsummaryrefslogtreecommitdiffstatsci
path: root/Samples/common
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2021-06-07 15:05:30 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2021-06-07 15:05:30 +0200
commit263de03914a3de5d55c4508c322e32d36caf2a87 (patch)
treec279464365fd7683744fa59d01b242e4b3ab17b9 /Samples/common
parentf738cf0f78c57750cce8fd5744825b0591f24bb8 (diff)
suggestion in IML editor, editor base classe for ShowCase and LogAnalyzerSimplifiedTabView
Diffstat (limited to 'Samples/common')
-rw-r--r--Samples/common/src/Editor.cs120
-rw-r--r--Samples/common/src/Extensions.cs27
-rw-r--r--Samples/common/src/ImlParsing/Syntax.cs14
-rw-r--r--Samples/common/src/ImlParsing/TokenType.cs4
-rw-r--r--Samples/common/src/ImlParsing/XmlSource.cs13
-rw-r--r--Samples/common/src/SampleBase.cs6
-rw-r--r--Samples/common/src/SampleBaseForEditor.cs312
-rw-r--r--Samples/common/ui/Interfaces/Experimental/DockWindow.template39
-rw-r--r--Samples/common/ui/Interfaces/Experimental/multiColorPick.crow2
-rw-r--r--Samples/common/ui/Interfaces/Wrapper/1.1.crow2
-rwxr-xr-xSamples/common/ui/icons/event.pngbin0 -> 241 bytes
-rwxr-xr-xSamples/common/ui/icons/property.pngbin0 -> 257 bytes
-rw-r--r--Samples/common/ui/templates/Suggestions.template8
13 files changed, 478 insertions, 69 deletions
diff --git a/Samples/common/src/Editor.cs b/Samples/common/src/Editor.cs
index b9b0ae90..7b9d5c10 100644
--- a/Samples/common/src/Editor.cs
+++ b/Samples/common/src/Editor.cs
@@ -11,6 +11,7 @@ using System.Threading.Tasks;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
+using System.Collections;
namespace Crow
{
@@ -30,8 +31,8 @@ namespace Crow
}
ListBox overlay;
- List<String> suggestions;
- public List<String> Suggestions {
+ IList suggestions;
+ public IList Suggestions {
get => suggestions;
set {
suggestions = value;
@@ -49,19 +50,25 @@ namespace Crow
.Select (s => s.Name).ToArray ();
- string [] getAllCrowTypeMembers (string crowTypeName) {
+ IEnumerable<MemberInfo> getAllCrowTypeMembers (string crowTypeName) {
Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName);
return crowType.GetMembers (BindingFlags.Public | BindingFlags.Instance).
Where (m=>((m is PropertyInfo pi && pi.CanWrite) || (m is EventInfo)) &&
- m.GetCustomAttribute<XmlIgnoreAttribute>() == null).Select (mb=>mb.Name).ToArray();
+ m.GetCustomAttribute<XmlIgnoreAttribute>() == null);
}
+ MemberInfo getCrowTypeMember (string crowTypeName, string memberName) {
+ Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName);
+ return crowType.GetMember (memberName, BindingFlags.Public | BindingFlags.Instance).FirstOrDefault ();
+ }
+
public override void OnTextChanged(object sender, TextChangeEventArgs e)
{
base.OnTextChanged(sender, e);
//Task.Run(()=>parse());
parse();
- tryGetSuggestions ();
+ if (HasFocus)
+ tryGetSuggestions ();
//Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}");
}
@@ -80,13 +87,54 @@ namespace Crow
Suggestions = allWidgetNames.Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
} else if (currentNode is AttributeSyntax attribNode) {
if (currentNode.Parent is ElementTagSyntax eltTag) {
- if (currentToken.Type == TokenType.AttributeName && eltTag.NameToken.HasValue) {
- Suggestions = getAllCrowTypeMembers (eltTag.NameToken.Value.AsString (_text))
- .Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ if (eltTag.NameToken.HasValue) {
+ if (currentToken.Type == TokenType.AttributeName) {
+ Suggestions = getAllCrowTypeMembers (eltTag.NameToken.Value.AsString (_text))
+ .Where (s => s.Name.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ } else if (attribNode.NameToken.HasValue) {
+ if (currentToken.Type == TokenType.AttributeValue) {
+ MemberInfo mi = getCrowTypeMember (
+ eltTag.NameToken.Value.AsString (_text), attribNode.NameToken.Value.AsString (_text));
+ if (mi is PropertyInfo pi) {
+ if (pi.PropertyType.IsEnum)
+ Suggestions = Enum.GetNames (pi.PropertyType)
+ .Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ else if (pi.PropertyType == typeof(bool))
+ Suggestions = (new string[] {"true", "false"}).
+ Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ else if (pi.PropertyType == typeof (Measure))
+ Suggestions = (new string[] {"Stretched", "Fit"}).
+ Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ else if (pi.PropertyType == typeof (Fill))
+ Suggestions = FastEnumUtility.FastEnum.GetValues<Colors> ()
+ .Where (s => s.ToString().StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ }
+ } else if (currentToken.Type == TokenType.AttributeValueOpen) {
+ MemberInfo mi = getCrowTypeMember (
+ eltTag.NameToken.Value.AsString (_text), attribNode.NameToken.Value.AsString (_text));
+ if (mi is PropertyInfo pi) {
+ if (pi.PropertyType.IsEnum)
+ Suggestions = Enum.GetNames (pi.PropertyType).ToList ();
+ else if (pi.PropertyType == typeof(bool))
+ Suggestions = new List<string> (new string[] {"true", "false"});
+ else if (pi.PropertyType == typeof (Fill))
+ Suggestions = FastEnumUtility.FastEnum.GetValues<Colors> ().ToList ();
+ else if (pi.PropertyType == typeof (Measure))
+ Suggestions = new List<string> (new string[] {"Stretched", "Fit"});
+ }
+ }
+ }
}
}
- } else if (currentNode is ElementStartTagSyntax eltStartTag) {
- Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)).ToList ();
+ } else if (currentToken.Type != TokenType.AttributeValueClose &&
+ currentToken.Type != TokenType.EmptyElementClosing &&
+ currentToken.Type != TokenType.ClosingSign &&
+ currentNode is ElementStartTagSyntax eltStartTag) {
+ if (currentToken.Type == TokenType.AttributeName)
+ Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text))
+ .Where (s => s.Name.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList ();
+ else
+ Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)).ToList ();
} else {
/*SyntaxNode curNode = source.FindNodeIncludingPosition (pos);
Console.WriteLine ($"Current Node: {curNode}");
@@ -101,7 +149,35 @@ namespace Crow
lock (IFace.UpdateMutex) {
if (overlay == null) {
overlay = IFace.LoadIMLFragment<ListBox>(@"
- <ListBox Style='suggestionsListBox' Data='{Suggestions}' />
+ <ListBox Style='suggestionsListBox' Data='{Suggestions}' >
+ <ItemTemplate>
+ <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left'
+ Selected = '{Background=${ControlHighlight}}'
+ Unselected = '{Background=Transparent}'>
+ <Label Text='{}' HorizontalAlignment='Left' />
+ </ListItem>
+ </ItemTemplate>
+ <ItemTemplate DataType='MemberInfo'>
+ <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left'
+ Selected = '{Background=${ControlHighlight}}'
+ Unselected = '{Background=Transparent}'>
+ <HorizontalStack>
+ <Image Picture='{GetIcon}' Width='16' Height='16'/>
+ <Label Text='{Name}' HorizontalAlignment='Left' />
+ </HorizontalStack>
+ </ListItem>
+ </ItemTemplate>
+ <ItemTemplate DataType='Colors'>
+ <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left'
+ Selected = '{Background=${ControlHighlight}}'
+ Unselected = '{Background=Transparent}'>
+ <HorizontalStack>
+ <Widget Background='{}' Width='20' Height='14'/>
+ <Label Text='{}' HorizontalAlignment='Left' />
+ </HorizontalStack>
+ </ListItem>
+ </ItemTemplate>
+ </ListBox>
");
overlay.DataSource = this;
overlay.Loaded += (sender, arg) => (sender as ListBox).SelectedIndex = 0;
@@ -116,12 +192,25 @@ namespace Crow
overlay.IsVisible = false;
}
void completeToken () {
- string selectedSugg = overlay.SelectedItem as string;
+ string selectedSugg = overlay.SelectedItem is MemberInfo mi ?
+ mi.Name : overlay.SelectedItem?.ToString ();
if (selectedSugg == null)
return;
- if (currentToken.Type == TokenType.ElementOpen || currentToken.Type == TokenType.WhiteSpace)
+ if (currentToken.Type == TokenType.ElementOpen ||
+ currentToken.Type == TokenType.WhiteSpace ||
+ currentToken.Type == TokenType.AttributeValueOpen)
update (new TextChange (currentToken.End, 0, selectedSugg));
- else
+ else if (currentToken.Type == TokenType.AttributeName && currentNode is AttributeSyntax attrib) {
+ if (attrib.ValueToken.HasValue) {
+ TextChange tc = new TextChange (currentToken.Start, currentToken.Length, selectedSugg);
+ update (tc);
+ selectionStart = lines.GetLocation (attrib.ValueToken.Value.Start + tc.CharDiff + 1);
+ CurrentLoc = lines.GetLocation (attrib.ValueToken.Value.End + tc.CharDiff - 1);
+ } else {
+ update (new TextChange (currentToken.Start, currentToken.Length, selectedSugg + "=\"\""));
+ MoveLeft ();
+ }
+ } else
update (new TextChange (currentToken.Start, currentToken.Length, selectedSugg));
hideOverlay ();
}
@@ -129,6 +218,7 @@ namespace Crow
hideOverlay ();
base.onMouseDown (sender, e);
}
+
public override void onKeyDown(object sender, KeyEventArgs e)
{
if (suggestionsActive) {
@@ -149,6 +239,8 @@ namespace Crow
overlay.onKeyDown (this, e);
return;
case Key.Tab:
+ case Key.Enter:
+ case Key.KeypadEnter:
completeToken ();
return;
}
diff --git a/Samples/common/src/Extensions.cs b/Samples/common/src/Extensions.cs
new file mode 100644
index 00000000..b4b0a1e5
--- /dev/null
+++ b/Samples/common/src/Extensions.cs
@@ -0,0 +1,27 @@
+
+
+using System.Reflection;
+using Crow;
+
+namespace Samples {
+ public static class Extensions {
+
+ public static CommandGroup GetCommands (this System.IO.DirectoryInfo di) =>
+ new CommandGroup(
+ new Command ("Set as root", ()=> {SampleBaseForEditor.CurrentProgramInstance.CurrentDir = di.FullName;})
+ );
+ public static CommandGroup GetCommands (this System.IO.FileInfo fi) =>
+ new CommandGroup(
+ new Command ("Delete", (sender0) => {
+ MessageBox.ShowModal (SampleBaseForEditor.CurrentProgramInstance, MessageBox.Type.YesNo, $"Delete {fi.Name}?").Yes += (sender, e) => {
+ System.IO.File.Delete(fi.FullName);
+ Widget listContainer = ((sender0 as Widget).LogicalParent as Widget).DataSource as Widget;
+ (listContainer.Parent as Group).RemoveChild(listContainer);
+ };
+ })
+ );
+ public static Picture GetIcon (this MemberInfo mi)
+ => mi is EventInfo ? new BmpPicture("#Icons.event.png") : new BmpPicture("#Icons.property.png");
+
+ }
+} \ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/Syntax.cs b/Samples/common/src/ImlParsing/Syntax.cs
index fd410eaf..a38e1558 100644
--- a/Samples/common/src/ImlParsing/Syntax.cs
+++ b/Samples/common/src/ImlParsing/Syntax.cs
@@ -128,8 +128,12 @@ namespace Crow
Exceptions.Add (new SyntaxException ("Extra equal sign in attribute syntax", iter.Current));
else
attrib.EqualToken = iter.Current;
- else if (iter.Current.Type == TokenType.AttributeValue) {
- attrib.ValueToken = attrib.EndToken = iter.Current;
+ else if (iter.Current.Type == TokenType.AttributeValueOpen)
+ attrib.ValueOpenToken = iter.Current;
+ else if (iter.Current.Type == TokenType.AttributeValue)
+ attrib.ValueToken = iter.Current;
+ else if (iter.Current.Type == TokenType.AttributeValueClose) {
+ attrib.ValueCloseToken = attrib.EndToken = iter.Current;
CurrentNode = CurrentNode.Parent;
} else {
Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current));
@@ -232,7 +236,7 @@ namespace Crow
public XmlSource Source => Root.source;
public bool Contains (int pos) =>
EndToken.HasValue ?
- StartToken.Start <= pos && EndToken.Value.End >= pos : false;
+ StartToken.Start <= pos && EndToken.Value.End > pos : false;
public void Dump (int level = 0) {
Console.WriteLine ($"{new string('\t', level)}{this}");
@@ -303,8 +307,10 @@ namespace Crow
public class AttributeSyntax : SyntaxNode {
public Token? NameToken { get; internal set; }
public Token? EqualToken { get; internal set; }
+ public Token? ValueOpenToken { get; internal set; }
+ public Token? ValueCloseToken { get; internal set; }
public Token? ValueToken { get; internal set; }
public AttributeSyntax (Token startTok) : base (startTok) {}
- public override bool IsComplete => base.IsComplete & NameToken.HasValue & EqualToken.HasValue & ValueToken.HasValue;
+ public override bool IsComplete => base.IsComplete & NameToken.HasValue & EqualToken.HasValue & ValueToken.HasValue & ValueOpenToken.HasValue & ValueCloseToken.HasValue;
}
} \ No newline at end of file
diff --git a/Samples/common/src/ImlParsing/TokenType.cs b/Samples/common/src/ImlParsing/TokenType.cs
index f9d7bcc6..3289d5ae 100644
--- a/Samples/common/src/ImlParsing/TokenType.cs
+++ b/Samples/common/src/ImlParsing/TokenType.cs
@@ -26,7 +26,9 @@ namespace Crow
PI_End = 0x0402,// '?>'
Operator = 0x0800,
EqualSign = 0x0801,
- AttributeValue = 0x2000,
+ AttributeValue = 0x2000,
+ AttributeValueOpen = 0x2001,
+ AttributeValueClose = 0x2002,
Keyword = 0x1000,
ElementOpen = 0x0403,// '<'
EndElementOpen = 0x0404,// '</'
diff --git a/Samples/common/src/ImlParsing/XmlSource.cs b/Samples/common/src/ImlParsing/XmlSource.cs
index f520dcf3..f70aec24 100644
--- a/Samples/common/src/ImlParsing/XmlSource.cs
+++ b/Samples/common/src/ImlParsing/XmlSource.cs
@@ -25,9 +25,10 @@ namespace Crow
syntaxAnalyser.Process ();
sw.Stop();
- foreach (Token t in Tokens)
+ /*foreach (Token t in Tokens)
Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}");
- syntaxAnalyser.Root.Dump();
+ syntaxAnalyser.Root.Dump();*/
+
Console.WriteLine ($"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)");
foreach (SyntaxException ex in syntaxAnalyser.Exceptions)
Console.WriteLine ($"{ex}");
@@ -212,9 +213,13 @@ namespace Crow
case '\'':
case '"':
char q = reader.Read();
- if (reader.TryReadUntil (q))
+ addTok (ref reader, TokenType.AttributeValueOpen);
+ if (reader.TryReadUntil (q)) {
+ addTok (ref reader, TokenType.AttributeValue);
reader.Advance ();
- addTok (ref reader, TokenType.AttributeValue);
+ addTok (ref reader, TokenType.AttributeValueClose);
+ } else
+ addTok (ref reader, TokenType.AttributeValue);
break;
case '=':
reader.Advance();
diff --git a/Samples/common/src/SampleBase.cs b/Samples/common/src/SampleBase.cs
index 1e849d6f..0c381048 100644
--- a/Samples/common/src/SampleBase.cs
+++ b/Samples/common/src/SampleBase.cs
@@ -10,7 +10,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
-namespace Crow
+namespace Samples
{
public class SampleBase : Interface
{
@@ -356,6 +356,10 @@ namespace Crow
initCommands();
base.OnInitialized();
}
+ protected override void processDrawing(Crow.Cairo.Context ctx)
+ {
+ base.processDrawing(ctx);
+ }
public override bool OnKeyDown(Key key)
{
diff --git a/Samples/common/src/SampleBaseForEditor.cs b/Samples/common/src/SampleBaseForEditor.cs
new file mode 100644
index 00000000..4af718a2
--- /dev/null
+++ b/Samples/common/src/SampleBaseForEditor.cs
@@ -0,0 +1,312 @@
+// Copyright (c) 2013-2021 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow;
+using System.IO;
+using System.Text;
+using Crow.IML;
+using System.Runtime.CompilerServices;
+using Glfw;
+using System.Diagnostics;
+using Crow.Text;
+using System.Collections.Generic;
+using Encoding = System.Text.Encoding;
+
+namespace Samples
+{
+ public class SampleBaseForEditor : SampleBase
+ {
+ public static SampleBaseForEditor CurrentProgramInstance;
+
+ protected override void OnInitialized () {
+ initCommands ();
+
+ base.OnInitialized ();
+
+ if (string.IsNullOrEmpty (CurrentDir))
+ CurrentDir = Path.Combine (Directory.GetCurrentDirectory (), "Interfaces");
+
+ }
+
+ protected const string _defaultFileName = "unnamed.txt";
+ protected string source = "", origSource;
+ protected TextBox editor;
+ bool debugLogRecording;
+
+
+ public string CurrentDir {
+ get => Configuration.Global.Get<string> (nameof (CurrentDir));
+ set {
+ if (CurrentDir == value)
+ return;
+ Configuration.Global.Set (nameof (CurrentDir), value);
+ NotifyValueChanged (CurrentDir);
+ }
+ }
+ public string CurrentFile {
+ get => Configuration.Global.Get<string> (nameof (CurrentFile));
+ set {
+ if (CurrentFile == value)
+ return;
+ Configuration.Global.Set (nameof (CurrentFile), value);
+ NotifyValueChanged (CurrentFile);
+ }
+ }
+ public virtual string Source {
+ get => source;
+ set {
+ if (source == value)
+ return;
+ source = value;
+ CMDSave.CanExecute = IsDirty;
+ NotifyValueChanged (source);
+ NotifyValueChanged ("IsDirty", IsDirty);
+ }
+ }
+ public bool DebugLoggingEnabled => DbgLogger.IsEnabled;
+
+ public DbgEvtType RecordedEvents {
+ get => Configuration.Global.Get<DbgEvtType> (nameof (RecordedEvents));
+ set {
+ if (RecordedEvents == value)
+ return;
+ Configuration.Global.Set (nameof (RecordedEvents), value);
+ if (DebugLogRecording)
+ DbgLogger.IncludeEvents = RecordedEvents;
+ NotifyValueChanged(RecordedEvents);
+ }
+ }
+ public DbgEvtType DiscardedEvents {
+ get => Configuration.Global.Get<DbgEvtType> (nameof (DiscardedEvents));
+ set {
+ if (DiscardedEvents == value)
+ return;
+ Configuration.Global.Set (nameof (DiscardedEvents), value);
+ if (DebugLogRecording)
+ DbgLogger.DiscardEvents = DiscardedEvents;
+ NotifyValueChanged(DiscardedEvents);
+ }
+ }
+ public bool DebugLogRecording {
+ get => debugLogRecording;
+ set {
+ if (debugLogRecording == value)
+ return;
+ debugLogRecording = value;
+ NotifyValueChanged(debugLogRecording);
+ }
+ }
+
+ public new bool IsDirty => source != origSource;
+
+
+ public Command CMDNew, CMDOpen, CMDSave, CMDSaveAs, CMDQuit, CMDShowLeftPane,
+ CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions;
+ public CommandGroup EditorCommands => new CommandGroup (CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDSave, CMDSaveAs);
+ protected virtual void initCommands ()
+ {
+ CMDNew = new Command ("New", new Action (onNewFile), "#Icons.blank-file.svg");
+ CMDSave = new Command ("Save", new Action (onSave), "#Icons.save.svg", false);
+ CMDSaveAs = new Command ("Save As...", new Action (onSaveAs), "#Icons.save.svg");
+ CMDQuit = new Command ("Quit", new Action (() => base.Quit ()), "#Icons.exit.svg");
+ CMDUndo = new Command ("Undo", new Action (undo),"#Icons.undo.svg", false);
+ CMDRedo = new Command ("Redo", new Action (redo),"#Icons.redo.svg", false);
+ CMDCut = new Command ("Cut", new Action (cut), "#Icons.scissors.svg", false);
+ CMDCopy = new Command ("Copy", new Action (copy), "#Icons.copy-file.svg", false);
+ CMDPaste= new Command ("Paste", new Action (paste), "#Icons.paste-on-document.svg", false);
+ }
+
+
+ protected Stack<TextChange> undoStack = new Stack<TextChange> ();
+ protected Stack<TextChange> redoStack = new Stack<TextChange> ();
+ protected TextSpan selection;
+ protected string SelectedText =>
+ selection.IsEmpty ? "" : Source.AsSpan (selection.Start, selection.Length).ToString ();
+
+ protected void undo () {
+ if (undoStack.TryPop (out TextChange tch)) {
+ redoStack.Push (tch.Inverse (source));
+ CMDRedo.CanExecute = true;
+ apply (tch);
+ editor.SetCursorPosition (tch.End + tch.ChangedText.Length);
+ }
+ if (undoStack.Count == 0)
+ CMDUndo.CanExecute = false;
+ }
+ protected void redo () {
+ if (redoStack.TryPop (out TextChange tch)) {
+ undoStack.Push (tch.Inverse (source));
+ CMDUndo.CanExecute = true;
+ apply (tch);
+ editor.SetCursorPosition (tch.End + tch.ChangedText.Length);
+ }
+ if (redoStack.Count == 0)
+ CMDRedo.CanExecute = false;
+ }
+ protected void resetUndoRedo () {
+ undoStack.Clear ();
+ redoStack.Clear ();
+ CMDUndo.CanExecute = false;
+ CMDRedo.CanExecute = false;
+ }
+
+ protected void cut () {
+ copy ();
+ applyChange (new TextChange (selection.Start, selection.Length, ""));
+ }
+ protected void copy () {
+ Clipboard = SelectedText;
+ }
+ protected void paste () {
+ applyChange (new TextChange (selection.Start, selection.Length, Clipboard));
+ }
+ protected void onNewFile () {
+ if (IsDirty) {
+ MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?").Yes += (sender, e) => newFile ();
+ } else
+ newFile ();
+ }
+ protected void onSave ()
+ {
+ if (!File.Exists (CurrentFile)) {
+ onSaveAs ();
+ return;
+ }
+ save ();
+ }
+ protected void onSaveAs ()
+ {
+ string dir = Path.GetDirectoryName (CurrentFile);
+ if (string.IsNullOrEmpty (dir))
+ dir = Directory.GetCurrentDirectory ();
+ LoadIMLFragment (@"<FileDialog Width='60%' Height='50%' Caption='Save as ...' CurrentDirectory='" +
+ dir + "' SelectedFile='" +
+ Path.GetFileName(CurrentFile) + "' OkClicked='saveFileDialog_OkClicked'/>").DataSource = this;
+ }
+ protected void saveFileDialog_OkClicked (object sender, EventArgs e)
+ {
+ FileDialog fd = sender as FileDialog;
+
+ if (string.IsNullOrEmpty (fd.SelectedFileFullPath))
+ return;
+
+ if (File.Exists(fd.SelectedFileFullPath)) {
+ MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "File exists, overwrite?");
+ mb.Yes += (sender2, e2) => {
+ CurrentFile = fd.SelectedFileFullPath;
+ save ();
+ };
+ return;
+ }
+
+ CurrentFile = fd.SelectedFileFullPath;
+ save ();
+ }
+
+ protected void newFile()
+ {
+ disableTextChangedEvent = true;
+ Source = @"<Label Text='Hello World' Background='MediumSeaGreen' Margin='10'/>";
+ disableTextChangedEvent = false;
+ resetUndoRedo ();
+ if (!string.IsNullOrEmpty (CurrentFile))
+ CurrentFile = Path.Combine (Path.GetDirectoryName (CurrentFile), "newfile.crow");
+ else
+ CurrentFile = Path.Combine (CurrentDir, "newfile.crow");
+ }
+
+ protected void save () {
+ using (Stream s = new FileStream(CurrentFile, FileMode.Create)) {
+ s.WriteByte (0xEF);
+ s.WriteByte (0xBB);
+ s.WriteByte (0xBF);
+ byte [] buff = Encoding.UTF8.GetBytes (source);
+ s.Write (buff, 0, buff.Length);
+ }
+ origSource = source;
+ NotifyValueChanged ("IsDirty", IsDirty);
+ CMDSave.CanExecute = false;
+ }
+
+ protected void reloadFromFile () {
+ disableTextChangedEvent = true;
+ if (File.Exists (CurrentFile)) {
+ using (Stream s = new FileStream (CurrentFile, FileMode.Open)) {
+ using (StreamReader sr = new StreamReader (s))
+ Source = origSource = sr.ReadToEnd ();
+ }
+ }
+ disableTextChangedEvent = false;
+ resetUndoRedo ();
+ }
+ protected bool disableTextChangedEvent = false;
+ protected void apply (TextChange change) {
+ Span<char> tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)];
+ ReadOnlySpan<char> src = source.AsSpan ();
+ src.Slice (0, change.Start).CopyTo (tmp);
+ if (!string.IsNullOrEmpty (change.ChangedText))
+ change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start));
+ src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length));
+ disableTextChangedEvent = true;
+ Source = tmp.ToString ();
+ disableTextChangedEvent = false;
+ }
+ protected void applyChange (TextChange change) {
+ undoStack.Push (change.Inverse (source));
+ redoStack.Clear ();
+ CMDUndo.CanExecute = true;
+ CMDRedo.CanExecute = false;
+ apply (change);
+ }
+ public void goUpDirClick (object sender, MouseButtonEventArgs e)
+ {
+ string root = Directory.GetDirectoryRoot (CurrentDir);
+ if (CurrentDir == root)
+ return;
+ CurrentDir = Directory.GetParent (CurrentDir).FullName;
+ }
+
+ protected void Dv_SelectedItemChanged (object sender, SelectionChangeEventArgs e)
+ {
+ FileSystemInfo fi = e.NewValue as FileSystemInfo;
+ if (fi == null)
+ return;
+ if (fi is DirectoryInfo)
+ return;
+
+ if (IsDirty) {
+ MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?");
+ mb.Yes += (mbsender, mbe) => { CurrentFile = fi.FullName; reloadFromFile (); };
+ return;
+ }
+
+ CurrentFile = fi.FullName;
+ reloadFromFile ();
+ }
+ protected void onTextChanged (object sender, TextChangeEventArgs e) {
+ if (disableTextChangedEvent)
+ return;
+ applyChange (e.Change);
+ }
+
+ protected void onSelectedTextChanged (object sender, EventArgs e) {
+ selection = (sender as Label).Selection;
+ Console.WriteLine($"selection:{selection.Start} length:{selection.Length}");
+ CMDCut.CanExecute = CMDCopy.CanExecute = !selection.IsEmpty;
+ }
+ protected void textView_KeyDown (object sender, Crow.KeyEventArgs e) {
+ if (Ctrl) {
+ if (e.Key == Glfw.Key.W) {
+ if (Shift)
+ CMDRedo.Execute ();
+ else
+ CMDUndo.Execute ();
+ } else if (e.Key == Glfw.Key.S) {
+ onSave ();
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Samples/common/ui/Interfaces/Experimental/DockWindow.template b/Samples/common/ui/Interfaces/Experimental/DockWindow.template
deleted file mode 100644
index 6c91312b..00000000
--- a/Samples/common/ui/Interfaces/Experimental/DockWindow.template
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0"?>
-<!--<Widget Background="{./Background}"/>-->
-<Border BorderWidth="1" Foreground="Black" CornerRadius="{./CornerRadius}"
- Background="{./Background}"
- MouseEnter="./onBorderMouseEnter"
- MouseLeave="./onBorderMouseLeave">
- <VerticalStack Spacing="0">
- <HorizontalStack Height="Fit">
- <Label Text="{./Name}" TextAlignment="Left" Width="Fit"
- Foreground="White" />
- <Label Text="{./DockingPosition}" TextAlignment="Left" Width="Fit"
- Foreground="White" />
- </HorizontalStack>
- <Label Text="{./Width}" TextAlignment="Left" Width="Fit"
- Foreground="White" />
- <Label Text="{./Height}" TextAlignment="Left" Width="Fit"
- Foreground="White" />
-
- <HorizontalStack Visible="{./IsDocked}" Height="Fit" Margin="1" Background="Grey">
- <Label Text="{./Caption}" TextAlignment="Left" Width="Stretched"
- Foreground="Jet" />
- <Image Width="8" Height="8" Focusable="true" Margin="0" Path="#Crow.Icons.exit2.svg"
- MouseClick="./butQuitPress"/>
- </HorizontalStack>
- <HorizontalStack Background="vgradient|0:0.5,0.6,0.5,0.5|1:0.2,0.3,0.3,0.7"
- Name="hs" Margin="0" Spacing="0" Height="Fit" Visible="{./IsFloating}">
- <Widget Width="5"/>
- <Image Margin="1" Width="10" Height="10" Path="{./Icon}"/>
- <Label Width="Stretched" Foreground="White" Margin="1" TextAlignment="Left" Text="{./Caption}" />
- <Border CornerRadius="6" BorderWidth="1" Foreground="Transparent" Height="10" Width="10"
- MouseEnter="{Foreground=White}" MouseLeave="{Foreground=Transparent}">
- <Image Focusable="true" Name="Image" Margin="0" Path="#Crow.Icons.exit2.svg"
- MouseClick="./butQuitPress"/>
- </Border>
- <Widget Width="5"/>
- </HorizontalStack>
- <Container Name="Content" MinimumSize="50,50"/>
- </VerticalStack>
-</Border>
diff --git a/Samples/common/ui/Interfaces/Experimental/multiColorPick.crow b/Samples/common/ui/Interfaces/Experimental/multiColorPick.crow
index 5b01c069..e30dd7ae 100644
--- a/Samples/common/ui/Interfaces/Experimental/multiColorPick.crow
+++ b/Samples/common/ui/Interfaces/Experimental/multiColorPick.crow
@@ -19,7 +19,7 @@
</ColorPicker>
</Template>
</ColorPicker>
- <Border CornerRadius="5" Width="60" Height="40" Background="{../cp.CurrentColor}">
+ <Border CornerRadius="5" Margin="10" Width="Fit" Height="40" Background="{../cp.CurrentColor}">
<Label Text="{../../cp.CurrentColor}"/>
</Border>
</VerticalStack> \ No newline at end of file
diff --git a/Samples/common/ui/Interfaces/Wrapper/1.1.crow b/Samples/common/ui/Interfaces/Wrapper/1.1.crow
index e51cf308..6afbff21 100644
--- a/Samples/common/ui/Interfaces/Wrapper/1.1.crow
+++ b/Samples/common/ui/Interfaces/Wrapper/1.1.crow
@@ -8,7 +8,7 @@
<Widget Width="50%" Height="50" Background="SeaGreen"/>
<Widget Width="50" Height="50" Background="SeaGreen"/>
<Widget Width="50" Height="50" Background="SeaGreen"/>
- <Widget Width="50" Height="50%" Background="SeaGreen"/>
+ <Widget Width="50%" Height="50" Background="SeaGreen"/>
<Widget Width="50" Height="50" Background="SeaGreen"/>
</Wrapper>
</Window> \ No newline at end of file
diff --git a/Samples/common/ui/icons/event.png b/Samples/common/ui/icons/event.png
new file mode 100755
index 00000000..8a27233f
--- /dev/null
+++ b/Samples/common/ui/icons/event.png
Binary files differ
diff --git a/Samples/common/ui/icons/property.png b/Samples/common/ui/icons/property.png
new file mode 100755
index 00000000..1226c16f
--- /dev/null
+++ b/Samples/common/ui/icons/property.png
Binary files differ
diff --git a/Samples/common/ui/templates/Suggestions.template b/Samples/common/ui/templates/Suggestions.template
index 5a4349d7..8bc55747 100644
--- a/Samples/common/ui/templates/Suggestions.template
+++ b/Samples/common/ui/templates/Suggestions.template
@@ -1,13 +1,13 @@
<?xml version="1.0"?>
-<Border BorderWidth="1" Background="{./Background}" Height="Stretched">
- <!--<HorizontalStack Margin="1">-->
+<Border BorderWidth="1" Background="{./Background}" Height="Stretched" Focusable="false">
+ <HorizontalStack Margin="1">
<Scroller Name="ItemsScroller" Margin="2">
<VerticalStack Height="Fit" MinimumSize="10,10"
Name="ItemsContainer" Margin="0" VerticalAlignment="Top"/>
</Scroller>
- <!--<ScrollBar Name="scrollbar1" Value="{²../ItemsScroller.ScrollY}"
+ <ScrollBar Name="scrollbar1" Value="{²../ItemsScroller.ScrollY}"
LargeIncrement="{../ItemsScroller.PageHeight}" SmallIncrement="30" CursorSize="{../ItemsScroller.ChildHeightRatio}"
Maximum="{../ItemsScroller.MaxScrollY}" Orientation="Vertical"
Width="12" />
- </HorizontalStack>-->
+ </HorizontalStack>
</Border>