diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/CustomRenderers/QueryEditorRenderer.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/CustomRenderers/QueryEditorRenderer.cs index cb566ca..c1525ab 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/CustomRenderers/QueryEditorRenderer.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/CustomRenderers/QueryEditorRenderer.cs @@ -7,7 +7,11 @@ // © Xamarin.Neo4j.iOS // +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; using CoreGraphics; +using Foundation; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; @@ -19,12 +23,52 @@ namespace Xamarin.Neo4j.iOS.CustomRenderers { public class QueryEditorRenderer : EditorRenderer { + private readonly string[] _keyWords = + { + // Clauses + "CALL", "CREATE", "DELETE", "DETACH", "FOREACH", "LOAD", "MATCH", "MERGE", "OPTIONAL", "REMOVE", "RETURN", "SET", "START", "UNION", "UNWIND", "WITH", + + // Subclauses + "LIMIT", "ORDER", "SKIP", "WHERE", "YIELD", + + // Modifiers + "ASC", "ASCENDING", "ASSERT", "BY", "CSV", "DESC", "DESCENDING", "ON", + + // Expressions + "ALL", "CASE", "COUNT", "ELSE", "END", "EXISTS", "THEN", "WHEN", + + // Operators + "AND", "AS", "CONTAINS", "DISTINCT", "ENDS", "IN", "IS", "NOT", "OR", "STARTS", "XOR", + + // Schema + "CONSTRAINT", "CREATE", "DROP", "EXISTS", "INDEX", "NODE", "KEY", "UNIQUE", + + // Hints + "INDEX", "JOIN", "SCAN", "USING", + + // Literals + "FALSE", "NULL", "TRUE" + }; + protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); + + if (e.NewElement != null) + { + Element.TextChanged += (sender, args) => + { + HighlightWords(_keyWords); + }; + } if (Control != null && Element != null) { + Control.AutocorrectionType = UITextAutocorrectionType.No; + Control.AutocapitalizationType = UITextAutocapitalizationType.None; + Control.SpellCheckingType = UITextSpellCheckingType.No; + Control.KeyboardType = UIKeyboardType.Default; + var executeButton = new UIBarButtonItem("Execute", UIBarButtonItemStyle.Done, (sender, args) => { if (Element is QueryEditor queryEditor) @@ -35,15 +79,85 @@ namespace Xamarin.Neo4j.iOS.CustomRenderers } }); - var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f)); - toolbar.Items = new[] + var keyButtons = new[] { + new UIBarButtonItem("(", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("(")), + new UIBarButtonItem(")", UIBarButtonItemStyle.Plain, (sender, args) => InsertText(")")), + new UIBarButtonItem("[", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("[")), + new UIBarButtonItem("]", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("]")), + new UIBarButtonItem(":", UIBarButtonItemStyle.Plain, (sender, args) => InsertText(":")), + new UIBarButtonItem("-", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("-")), + new UIBarButtonItem("\u2192", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("->")), + new UIBarButtonItem("\u2190", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("<-")), + new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace), + executeButton }; + + var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f)); + toolbar.Items = keyButtons; Control.InputAccessoryView = toolbar; } } + + private void HighlightWords(IEnumerable wordsToHighlight) + { + var text = PreprocessText(Control.Text); + var attributedText = new NSMutableAttributedString(text); + + // Iterate through the array and apply formatting to the editor's text. + foreach (var word in wordsToHighlight) + { + var regex = new Regex("\\b" + Regex.Escape(word) + "\\b", RegexOptions.IgnoreCase); + + foreach (Match match in regex.Matches(text)) + { + attributedText.AddAttribute(UIStringAttributeKey.ForegroundColor, Color.FromHex("#89982e").ToUIColor(), + new NSRange(match.Index, match.Length)); + } + } + + // Apply text color formatting for text inside single quotes. + ApplyQuoteTextColorFormatting(text, attributedText, "'(.*?)'", Color.FromHex("#ae8b2d").ToUIColor()); + + // Apply text color formatting for text inside double quotes. + ApplyQuoteTextColorFormatting(text, attributedText, "\"(.*?)\"", Color.FromHex("#ae8b2d").ToUIColor()); + + attributedText.AddAttribute(UIStringAttributeKey.Font, UIFont.SystemFontOfSize((nfloat) Element.FontSize), new NSRange(0, attributedText.Length)); + + Control.AttributedText = attributedText; + } + + private void InsertText(string text) + { + Control.InsertText(text); + } + + private static void ApplyQuoteTextColorFormatting(string text, NSMutableAttributedString attributedText, + string quotePattern, UIColor color) + { + var quoteRegex = new Regex(quotePattern); + + foreach (Match match in quoteRegex.Matches(text)) + { + attributedText.AddAttribute(UIStringAttributeKey.ForegroundColor, color, + new NSRange(match.Index, match.Length)); + } + } + + private static string PreprocessText(string text) + { + // Replace single quotation marks and their curly variants with: ' + text = text.Replace("‘", "'"); + text = text.Replace("’", "'"); + + // Replace double quotation marks and their curly variants with: " + text = text.Replace("“", "\""); + text = text.Replace("”", "\""); + + return text; + } } } diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Info.plist b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Info.plist index 472017e..3e8d158 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Info.plist +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Info.plist @@ -27,7 +27,9 @@ CFBundleIdentifier nl.resoftware.pocketgraph CFBundleVersion - 1.0.3 + 4 + CFBundleShortVersionString + 1.1.0 UILaunchStoryboardName LaunchScreen CFBundleName diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Properties/AssemblyInfo.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Properties/AssemblyInfo.cs index d835346..c22be1a 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Properties/AssemblyInfo.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Xamarin.Forms; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -26,4 +27,6 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file +[assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: ExportFont("RobotoMono-Regular.ttf", Alias = "RobotoMonoRegular")] diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/ScreenSizeService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/ScreenSizeService.cs index 91fc48f..91d137a 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/ScreenSizeService.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/ScreenSizeService.cs @@ -11,6 +11,7 @@ using UIKit; using Xamarin.Forms; using Xamarin.Neo4j.iOS.Services; using Xamarin.Neo4j.Services; +using Xamarin.Neo4j.Services.Interfaces; [assembly: Dependency(typeof(ScreenSizeService))] namespace Xamarin.Neo4j.iOS.Services diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/TrustManagerService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/TrustManagerService.cs index f33d843..e263687 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/TrustManagerService.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/TrustManagerService.cs @@ -11,6 +11,7 @@ using Neo4j.Driver; using Xamarin.Forms; using Xamarin.Neo4j.iOS.Services; using Xamarin.Neo4j.Services; +using Xamarin.Neo4j.Services.Interfaces; [assembly: Dependency(typeof(TrustManagerService))] namespace Xamarin.Neo4j.iOS.Services diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/VersionService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/VersionService.cs new file mode 100644 index 0000000..dab2275 --- /dev/null +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Services/VersionService.cs @@ -0,0 +1,24 @@ +// +// VersionService.cs +// +// Trevi Awater +// 02-03-2024 +// +// © Xamarin.Neo4j.iOS +// + +using Foundation; +using Xamarin.Forms; +using Xamarin.Neo4j.iOS.Services; +using Xamarin.Neo4j.Services.Interfaces; + +[assembly: Dependency(typeof(VersionService))] +namespace Xamarin.Neo4j.iOS.Services +{ + public class VersionService : IVersionService + { + public string GetVersion() => NSBundle.MainBundle.InfoDictionary["CFBundleShortVersionString"]?.ToString(); + + public string GetBuild() => NSBundle.MainBundle.InfoDictionary["CFBundleVersion"]?.ToString(); + } +} diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Xamarin.Neo4j.iOS.csproj b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Xamarin.Neo4j.iOS.csproj index 0257bc3..2afcca3 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Xamarin.Neo4j.iOS.csproj +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.iOS/Xamarin.Neo4j.iOS.csproj @@ -100,6 +100,7 @@ + diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Assets/Fonts/RobotoMono-Regular.ttf b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Assets/Fonts/RobotoMono-Regular.ttf new file mode 100644 index 0000000..6df2b25 Binary files /dev/null and b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Assets/Fonts/RobotoMono-Regular.ttf differ diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryEditor.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryEditor.cs index ba645ef..34f9afd 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryEditor.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryEditor.cs @@ -18,6 +18,11 @@ namespace Xamarin.Neo4j.Controls public event EventHandler ExecuteClicked; + public QueryEditor() + { + FontFamily = "RobotoMonoRegular"; + } + protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) { var sizeRequest = base.OnMeasure(widthConstraint, heightConstraint); diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SettingsPage.xaml b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SettingsPage.xaml index 5890d4a..b849655 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SettingsPage.xaml +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SettingsPage.xaml @@ -17,6 +17,7 @@ + diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IScreenSizeService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IScreenSizeService.cs index 0efbff7..c9d8e2f 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IScreenSizeService.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IScreenSizeService.cs @@ -1,4 +1,4 @@ -namespace Xamarin.Neo4j.Services +namespace Xamarin.Neo4j.Services.Interfaces { public interface IScreenSizeService { diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/ITrustManagerService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/ITrustManagerService.cs index 9731507..75ddf1e 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/ITrustManagerService.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/ITrustManagerService.cs @@ -1,6 +1,6 @@ using Neo4j.Driver; -namespace Xamarin.Neo4j.Services +namespace Xamarin.Neo4j.Services.Interfaces { public interface ITrustManagerService { diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IVersionService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IVersionService.cs new file mode 100644 index 0000000..bf6389a --- /dev/null +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Interfaces/IVersionService.cs @@ -0,0 +1,9 @@ +namespace Xamarin.Neo4j.Services.Interfaces +{ + public interface IVersionService + { + string GetVersion(); + + string GetBuild(); + } +} diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs index e1f499b..bf8e540 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs @@ -16,6 +16,7 @@ using Neo4jClient.Cypher; using Xamarin.Forms; using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Services; +using Xamarin.Neo4j.Services.Interfaces; using Xamarin.Neo4j.Utilities; [assembly: Dependency(typeof(Neo4jService))] diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs index 667a06a..b3a44cf 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs @@ -22,6 +22,7 @@ using Xamarin.Neo4j.Annotations; using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Pages; using Xamarin.Neo4j.Services; +using Xamarin.Neo4j.Services.Interfaces; using Xamarin.Neo4j.Utilities; namespace Xamarin.Neo4j.ViewModels diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SettingsViewModel.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SettingsViewModel.cs index 0dfe4e1..f30fd6a 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SettingsViewModel.cs +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SettingsViewModel.cs @@ -14,12 +14,17 @@ using Xamarin.Essentials; using Xamarin.Forms; using Xamarin.Neo4j.Annotations; using Xamarin.Neo4j.Pages; +using Xamarin.Neo4j.Services.Interfaces; namespace Xamarin.Neo4j.ViewModels { public class SettingsViewModel : ViewModelBase, INotifyPropertyChanged { + private string _versionLabel { get; set; } + public event PropertyChangedEventHandler PropertyChanged; + + private IVersionService _versionService { get; set; } public SettingsViewModel(INavigation navigation) : base(navigation) { @@ -32,6 +37,10 @@ namespace Xamarin.Neo4j.ViewModels { await Navigation.PushAsync(new LicensesPage()); })); + + _versionService = DependencyService.Get(); + + VersionLabel = $"Version: {_versionService.GetVersion()} (Build: {_versionService.GetBuild()})"; } [NotifyPropertyChangedInvocator] @@ -39,5 +48,22 @@ namespace Xamarin.Neo4j.ViewModels { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + #region Bindable Properties + + public string VersionLabel + { + get => _versionLabel; + + set + { + _versionLabel = value; + + OnPropertyChanged(nameof(VersionLabel)); + } + } + + #endregion + } } diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj index a9398f6..6e3a40d 100644 --- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj +++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj @@ -23,5 +23,7 @@ + + \ No newline at end of file