diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-07-27 15:25:20 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-07-27 15:25:20 +0300 |
| commit | 925bf377913f1003324d746b9e28e053186bfa29 (patch) | |
| tree | 34239e4e10738770c0d7375c7e5a28c2f1812415 /Software/Visual_Studio | |
| parent | 2418e272c79e617b84e589c14818ea3aa6b1a84a (diff) | |
| download | Tango-925bf377913f1003324d746b9e28e053186bfa29.tar.gz Tango-925bf377913f1003324d746b9e28e053186bfa29.zip | |
Procedures:
Fixed issue with TextBox on DialogWindow.
Fixed issue with multiple library linking.
Implemented error checking.
Many improvements.
Diffstat (limited to 'Software/Visual_Studio')
20 files changed, 989 insertions, 48 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs index 4dfb62cb7..5797d622a 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs @@ -19,5 +19,7 @@ namespace Tango.FSE.Procedures.Contracts void FocusCurrentEditor(); void ColorizeKeyword(String text); void ResetColrization(); + void HighlightError(int position, int length); + void ClearErrors(); } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DialogController.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DialogController.cs index 8d32f93c2..551b0d942 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DialogController.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DialogController.cs @@ -75,21 +75,28 @@ namespace Tango.FSE.Procedures public T FindControl<T>(String name) where T : DependencyObject { - bool completed = false; - T child = null; - - DispatcherProvider.Invoke(() => - { - child = _rootElement.FindChild<T>(name) as T; - completed = true; - }); - - while (!completed) + if (Thread.CurrentThread == Application.Current.Dispatcher.Thread) { - Thread.Sleep(10); + return _rootElement.FindChild<T>(name) as T; } + else + { + bool completed = false; + T child = null; + + DispatcherProvider.Invoke(() => + { + child = _rootElement.FindChild<T>(name) as T; + completed = true; + }); - return child; + while (!completed) + { + Thread.Sleep(10); + } + + return child; + } } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/ProcedureDialogEditorViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/ProcedureDialogEditorViewVM.cs index 8c95df985..264f52127 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/ProcedureDialogEditorViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/ProcedureDialogEditorViewVM.cs @@ -11,7 +11,11 @@ using System.Windows.Markup; using System.Xml; using System.Xml.Linq; using Tango.Core.Commands; +using Tango.Core.DI; using Tango.FSE.Common; +using Tango.FSE.Common.Notifications; +using Tango.FSE.Common.Storage; +using Tango.Settings; namespace Tango.FSE.Procedures.Dialogs { @@ -19,6 +23,12 @@ namespace Tango.FSE.Procedures.Dialogs { private FileSystemWatcher _watcher; + [TangoInject] + private INotificationProvider NotificationProvider { get; set; } + + [TangoInject] + private IStorageProvider StorageProvider { get; set; } + private String _name; public String Name { @@ -79,26 +89,49 @@ namespace Tango.FSE.Procedures.Dialogs public ProcedureDialogEditorViewVM() { + TangoIOC.Default.Inject(this); OpenInBlendCommand = new RelayCommand(OpenInBlend); } - private void OpenInBlend() + private async void OpenInBlend() { - if (this._watcher != null) + if (_watcher != null) { return; } - String blendPath = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Blend.exe"; + var settings = SettingsManager.Default.GetOrCreate<ProceduresSettings>(); - if (!File.Exists(blendPath)) + String blendPath = settings.BlendPath; + + if (String.IsNullOrWhiteSpace(blendPath) || !File.Exists(blendPath)) { - blendPath = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\Blend.exe"; + blendPath = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Blend.exe"; + + if (!File.Exists(blendPath)) + { + blendPath = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\Blend.exe"; + } } if (!File.Exists(blendPath)) { - throw new FileNotFoundException("Could not locate Blend for Visual Studio location."); + if (await NotificationProvider.ShowWarningQuestion("Could not locate Blend for Visual Studio on your computer.\nDo you wish to manually browse for it on your hard drive?", "BROWSE", "CANCEL")) + { + var r = await StorageProvider.OpenFile("Browse for Blend for Visual Studio", "Executables|*.exe"); + if (!r.Confirmed) + { + return; + } + + settings.BlendPath = r.SelectedItem; + settings.Save(); + blendPath = settings.BlendPath; + } + else + { + return; + } } var tempFolder = TemporaryManager.CreateFolder(); @@ -154,24 +187,30 @@ namespace Tango.FSE.Procedures.Dialogs _watcher.EnableRaisingEvents = true; - Task.Factory.StartNew(() => + await Task.Factory.StartNew(() => { var p = Process.Start(blendPath, Path.Combine(tempFolder, "ProcedureDialog.csproj")); p.WaitForExit(); - _watcher.Dispose(); + ClearWatcher(); }); } protected override void Accept() { - _watcher?.Dispose(); + ClearWatcher(); base.Accept(); } protected override void Cancel() { - _watcher?.Dispose(); + ClearWatcher(); base.Cancel(); } + + private void ClearWatcher() + { + _watcher?.Dispose(); + _watcher = null; + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs index 4469ff9da..c304c05d0 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs @@ -91,7 +91,7 @@ namespace Tango.FSE.Procedures /// Sends a request by name with optional comma separated arguments. /// </summary> /// <param name="messageName">Name of the message.</param> - /// <param name="timeout">The timeout.</param> + /// <param name="timeout">The timeout in seconds.</param> /// <param name="args">The arguments separated by commas.</param> /// <returns></returns> IMessage Send(String messageName, int? timeout = null, params Object[] args); @@ -109,7 +109,7 @@ namespace Tango.FSE.Procedures /// </summary> /// <typeparam name="T"></typeparam> /// <param name="messageName">Name of the message.</param> - /// <param name="timeout">The timeout.</param> + /// <param name="timeout">The timeout in seconds.</param> /// <param name="args">The arguments separated by commas.</param> /// <returns></returns> T Send<T>(String messageName, int? timeout = null, params Object[] args) where T : class, IMessage; @@ -127,7 +127,7 @@ namespace Tango.FSE.Procedures /// Sends the specified message. /// </summary> /// <param name="message">The message.</param> - /// <param name="timeout">The timeout.</param> + /// <param name="timeout">The timeout in seconds.</param> /// <returns></returns> IMessage Send(IMessage message, int? timeout = null); @@ -136,7 +136,7 @@ namespace Tango.FSE.Procedures /// </summary> /// <typeparam name="T"></typeparam> /// <param name="message">The message.</param> - /// <param name="timeout">The timeout.</param> + /// <param name="timeout">The timeout in seconds.</param> /// <returns></returns> T Send<T>(IMessage message, int? timeout = null) where T : class, IMessage; @@ -146,7 +146,7 @@ namespace Tango.FSE.Procedures /// <typeparam name="T"></typeparam> /// <param name="message">The message.</param> /// <param name="callback">Callback for continuous responses.</param> - /// <param name="timeout">The timeout.</param> + /// <param name="timeout">The timeout in seconds.</param> void SendContinuous<T>(IMessage message, Action<T> callback, int? timeout) where T : class, IMessage; /// <summary> @@ -155,7 +155,7 @@ namespace Tango.FSE.Procedures /// <typeparam name="T"></typeparam> /// <param name="messageName">Name of the message.</param> /// <param name="callback">Callback for continuous responses.</param> - /// <param name="timeout">The timeout.</param> + /// <param name="timeout">The timeout in seconds.</param> /// <param name="args">The arguments.</param> void SendContinuous<T>(String messageName, Action<T> callback, int? timeout, params Object[] args) where T : class, IMessage; @@ -363,12 +363,18 @@ namespace Tango.FSE.Procedures Thread RunAsync(Action action); /// <summary> - /// Loads the specified procedure dialog. + /// Loads the specified procedure dialog on the main UI thread. /// </summary> /// <param name="name">The name of the dialog file.</param> /// <returns></returns> IDialogController LoadDialog(String name); - IDialogController LoadDialogAsWindow(String name); + /// <summary> + /// Loads the specified procedure dialog as a window on the current thread. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="windowTitle">The window title</param> + /// <returns></returns> + IDialogController LoadDialogAsWindow(String name, String windowTitle); } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs index df314425c..f67749a92 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs @@ -564,7 +564,7 @@ namespace Tango.FSE.Procedures public IDialogController LoadDialog(string name) { - var dialog = _project.Dialogs.SingleOrDefault(x => x.Name == name); + var dialog = GetDialog(name); if (dialog == null) { throw new FileNotFoundException($"The specified dialog '{name}' could not be found in the project."); @@ -575,19 +575,25 @@ namespace Tango.FSE.Procedures return controller; } - public IDialogController LoadDialogAsWindow(string name) + public IDialogController LoadDialogAsWindow(string name, String windowTitle) { - var dialog = _project.Dialogs.SingleOrDefault(x => x.Name == name); + var dialog = GetDialog(name); if (dialog == null) { throw new FileNotFoundException($"The specified dialog '{name}' could not be found in the project."); } - WindowController controller = new WindowController(dialog.Xaml); + WindowController controller = new WindowController(dialog.Xaml, windowTitle); controller.Init(); return controller; } + private ProcedureDialog GetDialog(String name) + { + name = Path.GetFileNameWithoutExtension(name); + return _project.Dialogs.SingleOrDefault(x => x.Name.ToLower() == name.ToLower() + ".xaml"); + } + private void AutoInvoke(Task task) { bool completed = false; @@ -599,7 +605,7 @@ namespace Tango.FSE.Procedures completed = true; }); - DispatcherProvider.Invoke((Action)(async () => + DispatcherProvider.Invoke((Action)(async () => { await task; })); diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProceduresSettings.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProceduresSettings.cs new file mode 100644 index 000000000..0c885081f --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProceduresSettings.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Settings; + +namespace Tango.FSE.Procedures +{ + public class ProceduresSettings : SettingsBase + { + public String BlendPath { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj index 446ca7bde..f98771187 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj @@ -143,6 +143,7 @@ <Compile Include="IProcedureLogger.cs" /> <Compile Include="Messages\ProcedureProjectPublishedOrSuppressed.cs" /> <Compile Include="ProcedureDialog.cs" /> + <Compile Include="ProceduresSettings.cs" /> <Compile Include="ProjectRunner.cs" /> <Compile Include="ProjectRunnerState.cs" /> <Compile Include="Result.cs" /> @@ -177,6 +178,9 @@ <DependentUpon>ResultsView.xaml</DependentUpon> </Compile> <Compile Include="WindowController.cs" /> + <Compile Include="Windows\ProcedureDialogWindow.xaml.cs"> + <DependentUpon>ProcedureDialogWindow.xaml</DependentUpon> + </Compile> </ItemGroup> <ItemGroup> <Compile Include="Properties\AssemblyInfo.cs"> @@ -339,6 +343,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Windows\ProcedureDialogWindow.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> </ItemGroup> <ItemGroup> <Resource Include="Images\test_designer.png" /> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml index 0aa4727c5..06e60997d 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml @@ -453,4 +453,18 @@ </Setter> </Style> + <Style x:Key="ProcedureWindowTextBoxStyle" TargetType="TextBox"> + <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter> + <Setter Property="CaretBrush" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="BorderBrush" Value="{StaticResource FSE_BorderBrush}"></Setter> + <Setter Property="BorderThickness" Value="0 0 0 1"></Setter> + <Style.Triggers> + <Trigger Property="IsFocused" Value="True"> + <Setter Property="BorderBrush" Value="{StaticResource FSE_PrimaryAccentBrush}"></Setter> + <Setter Property="BorderThickness" Value="0 0 0 2"></Setter> + </Trigger> + </Style.Triggers> + </Style> + </ResourceDictionary>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs index 30e10fca5..859570b64 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs @@ -19,6 +19,7 @@ using Tango.Core.Commands; using Tango.Core.ExtensionMethods; using Tango.FSE.Common; using Tango.FSE.Common.Navigation; +using Tango.FSE.Common.Notifications; using Tango.FSE.Procedures.Contracts; using Tango.FSE.Procedures.Dialogs; using Tango.FSE.Procedures.Messages; @@ -293,6 +294,7 @@ namespace Tango.FSE.Procedures.ViewModels DecreaseFontSizeCommand = new RelayCommand(() => FontSize--); AddDialogCommand = new RelayCommand(AddProcedureDialog); EditDialogCommand = new RelayCommand<ProcedureDialog>(EditProcedureDialog); + RemoveDialogCommand = new RelayCommand<ProcedureDialog>(RemoveProcedureDialog); } #endregion @@ -449,11 +451,22 @@ namespace Tango.FSE.Procedures.ViewModels var result = await ProjectRunner.Compile(); CompilationErrors = result.Errors; + View.ClearErrors(); + if (CompilationErrors.Count > 0) { Logger.WriteLine("Project compiled with errors."); Status = "Project compiled with errors."; SelectedToolWindow = ToolWindows.Errors; + + foreach (var error in CompilationErrors) + { + if (error.File == SelectedScript.Name) + { + View.HighlightError(error.Position, error.Length); + } + } + return false; } else @@ -563,6 +576,19 @@ namespace Tango.FSE.Procedures.ViewModels { SelectedToolWindow = ToolWindows.Errors; } + + InvokeUI(() => + { + View.ClearErrors(); + + foreach (var error in CompilationErrors) + { + if (error.File == SelectedScript.Name) + { + View.HighlightError(error.Position, error.Length); + } + } + }); } _compileTimer.Start(); @@ -644,6 +670,8 @@ namespace Tango.FSE.Procedures.ViewModels Status = "Project saved."; + NotificationProvider.PushSnackbarItem(MessageType.Success, "Procedure project saved", false, $"Project '{Project.Name}' saved successfully.", TimeSpan.FromSeconds(1.5)); + Project.Scripts.ToList().ForEach(x => x.IsChanged = false); _isProjectChanged = false; } @@ -808,6 +836,12 @@ namespace Tango.FSE.Procedures.ViewModels if (result.Confirmed) { + if (Project.Scripts.Any(x => x != script && x.Name.ToLower() == result.Input.ToLower() + ".csx")) + { + await NotificationProvider.ShowError($"The project already contains a file named '{result.Input}'."); + return; + } + script.Name = result.Input + ".csx"; script.IsChanged = true; } @@ -1060,13 +1094,19 @@ namespace Tango.FSE.Procedures.ViewModels private async void AddProcedureDialog() { - var result = await NotificationProvider.ShowInputBox("New Procedure Dialog", "Please specify the dialog name", PackIconKind.WindowRestore, "MyDialog.xaml", "Dialog Name", 30); + var result = await NotificationProvider.ShowInputBox("New Procedure Dialog", "Please specify the dialog name", PackIconKind.WindowRestore, $"Dialog{Project.Dialogs.Count + 1}", "Dialog Name", 30); if (result.Confirmed) { + if (Project.Dialogs.Any(x => x.Name.ToLower() == result.Input.ToLower() + ".xaml")) + { + await NotificationProvider.ShowError($"The project already contains a dialog named '{result.Input}'."); + return; + } + Project.Dialogs.Add(new ProcedureDialog() { - Name = result.Input, + Name = result.Input + ".xaml", Xaml = Properties.Resources.dialog_template }); } @@ -1076,17 +1116,32 @@ namespace Tango.FSE.Procedures.ViewModels { var vm = await NotificationProvider.ShowDialog(new ProcedureDialogEditorViewVM() { - Name = dialog.Name, + Name = Path.GetFileNameWithoutExtension(dialog.Name), Xaml = dialog.Xaml, }); if (vm.DialogResult) { - dialog.Name = vm.Name; + if (Project.Dialogs.Any(x => x != dialog && x.Name.ToLower() == vm.Name.ToLower() + ".xaml")) + { + await NotificationProvider.ShowError($"The project already contains a dialog named '{vm.Name}'."); + return; + } + + dialog.Name = vm.Name + ".xaml"; dialog.Xaml = vm.Xaml; } } + private async void RemoveProcedureDialog(ProcedureDialog dialog) + { + if (await NotificationProvider.ShowWarningQuestion($"Are you sure you want to delete '{dialog.Name}'?")) + { + Project.Dialogs.Remove(dialog); + _isProjectChanged = true; + } + } + #endregion } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ResultsViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ResultsViewVM.cs index 2fa496fb7..dfad0017d 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ResultsViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ResultsViewVM.cs @@ -10,6 +10,7 @@ using Tango.Core.Commands; using Tango.Core.DI; using Tango.CSV; using Tango.FSE.Common; +using Tango.FSE.Common.Notifications; using Tango.FSE.Procedures.CSV; using Tango.FSE.Procedures.Dialogs; @@ -126,7 +127,10 @@ namespace Tango.FSE.Procedures.ViewModels } }); - await NotificationProvider.ShowSuccess("File exported successfully."); + NotificationProvider.PushSnackbarItem(MessageType.Success, "CSV file exported", true, "Procedure result was exported to file.\nTap to browse the file location.", TimeSpan.FromSeconds(5), null, () => + { + StorageProvider.ShowInExplorer(r.SelectedItem); + }); } catch (Exception ex) { diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs index 8033656c4..ee21509b2 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs @@ -127,5 +127,15 @@ namespace Tango.FSE.Procedures.Views { _vm.EditProcedureDialog((sender as FrameworkElement).DataContext as ProcedureDialog); } + + public void HighlightError(int position, int length) + { + GetCurrentEditor()?.HighlighError(position, length); + } + + public void ClearErrors() + { + GetAllEditors().ForEach(x => x.ClearErrors()); + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/WindowController.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/WindowController.cs index f70ce3cfd..f0efc9b83 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/WindowController.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/WindowController.cs @@ -10,9 +10,12 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Markup; +using System.Xml.Linq; using Tango.Core.DI; using Tango.FSE.Common.Notifications; using Tango.FSE.Common.Threading; +using Tango.FSE.Common.WindowsManager; +using Tango.FSE.Procedures.Windows; namespace Tango.FSE.Procedures { @@ -24,7 +27,9 @@ namespace Tango.FSE.Procedures private String _xaml; private FrameworkElement _rootElement; - private Window _dialogWindow; + private ProcedureDialogWindow _dialogWindow; + private Thread _windowThread; + private String _title; [TangoInject] private IDispatcherProvider DispatcherProvider { get; set; } @@ -37,30 +42,50 @@ namespace Tango.FSE.Procedures } } - internal WindowController(String xaml) + internal WindowController(String xaml, String title) { _xaml = xaml; + _title = title; TangoIOC.Default.Inject(this); } internal void Init() { + String xaml = _xaml; + + XDocument doc = XDocument.Parse(xaml); + var textBoxes = doc.Descendants().Where(x => x.Name.LocalName == "TextBox").ToList(); + textBoxes.ForEach(x => x.SetAttributeValue("Style", "{StaticResource ProcedureWindowTextBoxStyle}")); + xaml = doc.ToString(); + FrameworkElement rootElement; var stream = new MemoryStream(); var writer = new StreamWriter(stream); - writer.Write(_xaml); + writer.Write(xaml); writer.Flush(); stream.Position = 0; rootElement = (FrameworkElement)XamlReader.Load(stream); _rootElement = rootElement; stream.Dispose(); - _dialogWindow = new Window(); - _dialogWindow.Content = rootElement; + _dialogWindow = new ProcedureDialogWindow(); + _dialogWindow.Width = _rootElement.Width; + _dialogWindow.Height = _rootElement.Height; + _dialogWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen; + _dialogWindow.Title = _title; + _dialogWindow.SetContent(rootElement); + + //var textBoxesToRestore = rootElement.FindVisualChildren<TextBox>().ToList(); + //textBoxesToRestore.ForEach(x => x.Style = null); + var helper = new WindowInteropHelper(_dialogWindow); helper.EnsureHandle(); + _windowThread = _dialogWindow.Dispatcher.Thread; + + //System.Windows.Threading.Dispatcher.Run(); + //Make the main window as owner ? could be risky.. //IntPtr mainWindowHanle = IntPtr.Zero; @@ -80,7 +105,28 @@ namespace Tango.FSE.Procedures public T FindControl<T>(String name) where T : DependencyObject { - return _rootElement.FindChild<T>(name) as T; + if (Thread.CurrentThread == _windowThread) + { + return _rootElement.FindChild<T>(name) as T; + } + else + { + bool completed = false; + T child = null; + + DispatcherProvider.Invoke(() => + { + child = _rootElement.FindChild<T>(name) as T; + completed = true; + }); + + while (!completed) + { + Thread.Sleep(10); + } + + return child; + } } public void Close() diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Windows/ProcedureDialogWindow.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Windows/ProcedureDialogWindow.xaml new file mode 100644 index 000000000..58e29d055 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Windows/ProcedureDialogWindow.xaml @@ -0,0 +1,61 @@ +<mahapps:MetroWindow x:Class="Tango.FSE.Procedures.Windows.ProcedureDialogWindow" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:commonControls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common" + xmlns:local="clr-namespace:Tango.FSE.Procedures.Windows" + mc:Ignorable="d" + Title="Tango FSE" + TextElement.Foreground="{StaticResource FSE_PrimaryForegroundBrush}" + Background="{StaticResource FSE_PrimaryBackgroundBrush}" + TextElement.FontSize="{StaticResource FSE_DefaultFontSize}" + TitleCharacterCasing="Normal" + UseNoneWindowStyle="True" + EnableDWMDropShadow="True" + BorderThickness="1" + BorderBrush="Gray" + FontFamily="{StaticResource flexo}" + WindowTitleBrush="{StaticResource FSE_PrimaryBackgroundBrush}" + TitleForeground="{StaticResource FSE_PrimaryForegroundBrush}" + WindowTransitionsEnabled="False" d:DesignWidth="1000" d:DesignHeight="600"> + + <Grid> + <DockPanel> + <Grid Height="40" DockPanel.Dock="Top" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}"> + + <StackPanel Margin="5 0 0 0" HorizontalAlignment="Left" Orientation="Horizontal"> + <Image Source="{StaticResource FSE_Machine_Full}" RenderOptions.BitmapScalingMode="Fant" Margin="8" /> + <TextBlock FontWeight="SemiBold" Margin="5 0 0 0" VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=Title,FallbackValue='Tango FSE'}"></TextBlock> + </StackPanel> + + <Image Source="{StaticResource FSE_Twine_Logo_Colored}" RenderOptions.BitmapScalingMode="Fant" HorizontalAlignment="Center" Margin="5"></Image> + + <Polygon HorizontalAlignment="Right" Points="200,0 300,0 300,100 0,100" Width="250" Stretch="Fill"> + <Polygon.Fill> + <ImageBrush Viewport="0 0 800 300" ViewportUnits="Absolute" ImageSource="{StaticResource FSE_Abstract}" Stretch="None" Opacity="0.6" /> + </Polygon.Fill> + </Polygon> + </Grid> + <Grid Margin="0,-2,0,2"> + <Border x:Name="contentBorder" x:FieldModifier="public" BorderBrush="{StaticResource FSE_PrimaryBackgroundBrush}" BorderThickness="2"> + + </Border> + + <Image IsHitTestVisible="False" Source="{StaticResource FSE_Shadow_Top}" VerticalAlignment="Top" Stretch="Fill" Height="10" Opacity="0.6" /> + </Grid> + </DockPanel> + + <Grid VerticalAlignment="Top" Height="40"> + <DockPanel> + <UniformGrid DockPanel.Dock="Right" Columns="3" HorizontalAlignment="Right" Width="130"> + <commonControls:IconButton Padding="8" x:Name="btnMinimize" Icon="WindowMinimize" /> + <commonControls:IconButton Padding="8" x:Name="btnMaximize" Icon="WindowMaximize" /> + <commonControls:IconButton Padding="8" x:Name="btnClose" Icon="WindowClose" ToolTip="Close and attach to main application" /> + </UniformGrid> + <Grid Background="Transparent" x:Name="gridTitle" Margin="0 0 0 0" Height="50" VerticalAlignment="Top"></Grid> + </DockPanel> + </Grid> + </Grid> +</mahapps:MetroWindow> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Windows/ProcedureDialogWindow.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Windows/ProcedureDialogWindow.xaml.cs new file mode 100644 index 000000000..e3abad343 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Windows/ProcedureDialogWindow.xaml.cs @@ -0,0 +1,98 @@ +using MahApps.Metro.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Tango.FSE.Procedures.Windows +{ + /// <summary> + /// Interaction logic for ChildWindow.xaml + /// </summary> + public partial class ProcedureDialogWindow : MetroWindow + { + private Point _startPoint; + private bool _isMouseDown; + + public ProcedureDialogWindow() + { + InitializeComponent(); + + btnMinimize.Click += (_, __) => WindowState = WindowState.Minimized; + btnMaximize.Click += (_, __) => WindowState = WindowState == WindowState.Maximized ? WindowState = WindowState.Normal : WindowState = WindowState.Maximized; + btnClose.Click += (_, __) => Close(); + gridTitle.MouseLeftButtonDown += (x, e) => + { + Mouse.Capture(gridTitle); + + _startPoint = e.GetPosition(this); + + if (e.ClickCount > 1 && btnMaximize.IsEnabled) + { + WindowState = WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal; + return; + } + + _isMouseDown = true; + }; + gridTitle.MouseLeftButtonUp += (x, e) => + { + _isMouseDown = false; + gridTitle.ReleaseMouseCapture(); + }; + + gridTitle.MouseMove += GridTitle_MouseMove; + StateChanged += MainWindow_StateChanged; + } + + private void MainWindow_StateChanged(object sender, EventArgs e) + { + if (WindowState == WindowState.Maximized) + { + btnMaximize.Icon = MaterialDesignThemes.Wpf.PackIconKind.WindowRestore; + } + else if (WindowState == WindowState.Normal) + { + btnMaximize.Icon = MaterialDesignThemes.Wpf.PackIconKind.WindowMaximize; + } + } + + private void GridTitle_MouseMove(object sender, MouseEventArgs e) + { + if (_isMouseDown) + { + if (WindowState == WindowState.Maximized) + { + var previousWidth = Width; + WindowState = WindowState.Normal; + var currentWidth = Width; + var locationPrecentageBefore = _startPoint.X / previousWidth; + var newLocationX = currentWidth * locationPrecentageBefore; + _startPoint = new Point(newLocationX, _startPoint.Y); + } + else + { + Point pointToWindow = Mouse.GetPosition(this); + Point pointToScreen = PointToScreen(pointToWindow); + + Left = pointToScreen.X - _startPoint.X; + Top = pointToScreen.Y - _startPoint.Y; + } + } + } + + public void SetContent(FrameworkElement element) + { + contentBorder.Child = element; + } + } +} diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs index 7dcfcb3c8..d6e3a5b52 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs @@ -528,4 +528,20 @@ namespace Tango.FSE.Common } } + + public abstract class FSEViewModelWithModuleSettings<TView,TSettings> : FSEViewModel<TView> where TView : IFSEView where TSettings : SettingsBase + { + /// <summary> + /// Gets or sets the module settings. + /// </summary> + public TSettings ModuleSettings { get; private set; } + + /// <summary> + /// Initializes a new instance of the <see cref="FSEViewModelWithModuleSettings{T}"/> class. + /// </summary> + public FSEViewModelWithModuleSettings() + { + ModuleSettings = SettingsManager.Default.GetOrCreate<TSettings>(); + } + } }
\ No newline at end of file diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs index 13ebda6bb..8ca64ca18 100644 --- a/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs @@ -85,11 +85,12 @@ namespace Tango.Scripting.Basic foreach (var file in Scripts.Where(x => !x.IsEntryPoint && script != x).Select(x => Path.Combine(tempFolder, x.Name))) { - loadingString = $"#load \"{file}\"\n"; + loadingString += $"#load \"{file}\"\n"; script.LoadCount++; - script.LoadCharCount += loadingString.Length; } + script.LoadCharCount += loadingString.Length; + code = loadingString + code; if (!script.IsEntryPoint) diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Errors/ITextMarker.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Errors/ITextMarker.cs new file mode 100644 index 000000000..dcbf8388a --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Errors/ITextMarker.cs @@ -0,0 +1,169 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; + +namespace Tango.Scripting.Editors +{ + /// <summary> + /// Represents a text marker. + /// </summary> + public interface ITextMarker + { + /// <summary> + /// Gets the start offset of the marked text region. + /// </summary> + int StartOffset { get; } + + /// <summary> + /// Gets the end offset of the marked text region. + /// </summary> + int EndOffset { get; } + + /// <summary> + /// Gets the length of the marked region. + /// </summary> + int Length { get; } + + /// <summary> + /// Deletes the text marker. + /// </summary> + void Delete(); + + /// <summary> + /// Gets whether the text marker was deleted. + /// </summary> + bool IsDeleted { get; } + + /// <summary> + /// Event that occurs when the text marker is deleted. + /// </summary> + event EventHandler Deleted; + + /// <summary> + /// Gets/Sets the background color. + /// </summary> + Color? BackgroundColor { get; set; } + + /// <summary> + /// Gets/Sets the foreground color. + /// </summary> + Color? ForegroundColor { get; set; } + + /// <summary> + /// Gets/Sets the font weight. + /// </summary> + FontWeight? FontWeight { get; set; } + + /// <summary> + /// Gets/Sets the font style. + /// </summary> + FontStyle? FontStyle { get; set; } + + /// <summary> + /// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers. + /// </summary> + TextMarkerTypes MarkerTypes { get; set; } + + /// <summary> + /// Gets/Sets the color of the marker. + /// </summary> + Color MarkerColor { get; set; } + + /// <summary> + /// Gets/Sets an object with additional data for this text marker. + /// </summary> + object Tag { get; set; } + + /// <summary> + /// Gets/Sets an object that will be displayed as tooltip in the text editor. + /// </summary> + /// <remarks>Not supported in this sample!</remarks> + object ToolTip { get; set; } + } + + [Flags] + public enum TextMarkerTypes + { + /// <summary> + /// Use no marker + /// </summary> + None = 0x0000, + /// <summary> + /// Use squiggly underline marker + /// </summary> + SquigglyUnderline = 0x001, + /// <summary> + /// Normal underline. + /// </summary> + NormalUnderline = 0x002, + /// <summary> + /// Dotted underline. + /// </summary> + DottedUnderline = 0x004, + + /// <summary> + /// Horizontal line in the scroll bar. + /// </summary> + LineInScrollBar = 0x0100, + /// <summary> + /// Small triangle in the scroll bar, pointing to the right. + /// </summary> + ScrollBarRightTriangle = 0x0400, + /// <summary> + /// Small triangle in the scroll bar, pointing to the left. + /// </summary> + ScrollBarLeftTriangle = 0x0800, + /// <summary> + /// Small circle in the scroll bar. + /// </summary> + CircleInScrollBar = 0x1000 + } + + public interface ITextMarkerService + { + /// <summary> + /// Creates a new text marker. The text marker will be invisible at first, + /// you need to set one of the Color properties to make it visible. + /// </summary> + ITextMarker Create(int startOffset, int length); + + /// <summary> + /// Gets the list of text markers. + /// </summary> + IEnumerable<ITextMarker> TextMarkers { get; } + + /// <summary> + /// Removes the specified text marker. + /// </summary> + void Remove(ITextMarker marker); + + /// <summary> + /// Removes all text markers that match the condition. + /// </summary> + void RemoveAll(Predicate<ITextMarker> predicate); + + /// <summary> + /// Finds all text markers at the specified offset. + /// </summary> + IEnumerable<ITextMarker> GetMarkersAtOffset(int offset); + } +} diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Errors/TextMarkerService.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Errors/TextMarkerService.cs new file mode 100644 index 000000000..2bb3d8e03 --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Errors/TextMarkerService.cs @@ -0,0 +1,365 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using System.Windows.Threading; +using Tango.Scripting.Editors.Document; +using Tango.Scripting.Editors.Rendering; + +namespace Tango.Scripting.Editors +{ + /// <summary> + /// Handles the text markers for a code editor. + /// </summary> + public sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService, ITextViewConnect + { + TextSegmentCollection<TextMarker> markers; + TextDocument document; + + public TextMarkerService(TextDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + this.document = document; + this.markers = new TextSegmentCollection<TextMarker>(document); + } + + #region ITextMarkerService + public ITextMarker Create(int startOffset, int length) + { + if (markers == null) + throw new InvalidOperationException("Cannot create a marker when not attached to a document"); + + int textLength = document.TextLength; + if (startOffset < 0 || startOffset > textLength) + throw new ArgumentOutOfRangeException("startOffset", startOffset, "Value must be between 0 and " + textLength); + if (length < 0 || startOffset + length > textLength) + throw new ArgumentOutOfRangeException("length", length, "length must not be negative and startOffset+length must not be after the end of the document"); + + TextMarker m = new TextMarker(this, startOffset, length); + markers.Add(m); + // no need to mark segment for redraw: the text marker is invisible until a property is set + return m; + } + + public IEnumerable<ITextMarker> GetMarkersAtOffset(int offset) + { + if (markers == null) + return Enumerable.Empty<ITextMarker>(); + else + return markers.FindSegmentsContaining(offset); + } + + public IEnumerable<ITextMarker> TextMarkers { + get { return markers ?? Enumerable.Empty<ITextMarker>(); } + } + + public void RemoveAll(Predicate<ITextMarker> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + if (markers != null) { + foreach (TextMarker m in markers.ToArray()) { + if (predicate(m)) + Remove(m); + } + } + } + + public void Remove(ITextMarker marker) + { + if (marker == null) + throw new ArgumentNullException("marker"); + TextMarker m = marker as TextMarker; + if (markers != null && markers.Remove(m)) { + Redraw(m); + m.OnDeleted(); + } + } + + /// <summary> + /// Redraws the specified text segment. + /// </summary> + internal void Redraw(ISegment segment) + { + foreach (var view in textViews) { + view.Redraw(segment, DispatcherPriority.Normal); + } + if (RedrawRequested != null) + RedrawRequested(this, EventArgs.Empty); + } + + public event EventHandler RedrawRequested; + #endregion + + #region DocumentColorizingTransformer + protected override void ColorizeLine(DocumentLine line) + { + if (markers == null) + return; + int lineStart = line.Offset; + int lineEnd = lineStart + line.Length; + foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length)) { + Brush foregroundBrush = null; + if (marker.ForegroundColor != null) { + foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value); + foregroundBrush.Freeze(); + } + ChangeLinePart( + Math.Max(marker.StartOffset, lineStart), + Math.Min(marker.EndOffset, lineEnd), + element => { + if (foregroundBrush != null) { + element.TextRunProperties.SetForegroundBrush(foregroundBrush); + } + Typeface tf = element.TextRunProperties.Typeface; + element.TextRunProperties.SetTypeface(new Typeface( + tf.FontFamily, + marker.FontStyle ?? tf.Style, + marker.FontWeight ?? tf.Weight, + tf.Stretch + )); + } + ); + } + } + #endregion + + #region IBackgroundRenderer + public KnownLayer Layer { + get { + // draw behind selection + return KnownLayer.Selection; + } + } + + public void Draw(TextView textView, DrawingContext drawingContext) + { + if (textView == null) + throw new ArgumentNullException("textView"); + if (drawingContext == null) + throw new ArgumentNullException("drawingContext"); + if (markers == null || !textView.VisualLinesValid) + return; + var visualLines = textView.VisualLines; + if (visualLines.Count == 0) + return; + int viewStart = visualLines.First().FirstDocumentLine.Offset; + int viewEnd = visualLines.Last().LastDocumentLine.EndOffset; + foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) { + if (marker.BackgroundColor != null) { + BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); + geoBuilder.AlignToWholePixels = true; + geoBuilder.CornerRadius = 3; + geoBuilder.AddSegment(textView, marker); + Geometry geometry = geoBuilder.CreateGeometry(); + if (geometry != null) { + Color color = marker.BackgroundColor.Value; + SolidColorBrush brush = new SolidColorBrush(color); + brush.Freeze(); + drawingContext.DrawGeometry(brush, null, geometry); + } + } + var underlineMarkerTypes = TextMarkerTypes.SquigglyUnderline | TextMarkerTypes.NormalUnderline | TextMarkerTypes.DottedUnderline; + if ((marker.MarkerTypes & underlineMarkerTypes) != 0) { + foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) { + Point startPoint = r.BottomLeft; + Point endPoint = r.BottomRight; + + Brush usedBrush = new SolidColorBrush(marker.MarkerColor); + usedBrush.Freeze(); + if ((marker.MarkerTypes & TextMarkerTypes.SquigglyUnderline) != 0) { + double offset = 2.5; + + int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4); + + StreamGeometry geometry = new StreamGeometry(); + + using (StreamGeometryContext ctx = geometry.Open()) { + ctx.BeginFigure(startPoint, false, false); + ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false); + } + + geometry.Freeze(); + + Pen usedPen = new Pen(usedBrush, 1); + usedPen.Freeze(); + drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry); + } + if ((marker.MarkerTypes & TextMarkerTypes.NormalUnderline) != 0) { + Pen usedPen = new Pen(usedBrush, 1); + usedPen.Freeze(); + drawingContext.DrawLine(usedPen, startPoint, endPoint); + } + if ((marker.MarkerTypes & TextMarkerTypes.DottedUnderline) != 0) { + Pen usedPen = new Pen(usedBrush, 1); + usedPen.DashStyle = DashStyles.Dot; + usedPen.Freeze(); + drawingContext.DrawLine(usedPen, startPoint, endPoint); + } + } + } + } + } + + IEnumerable<Point> CreatePoints(Point start, Point end, double offset, int count) + { + for (int i = 0; i < count; i++) + yield return new Point(start.X + i * offset, start.Y - ((i + 1) % 2 == 0 ? offset : 0)); + } + #endregion + + #region ITextViewConnect + readonly List<TextView> textViews = new List<TextView>(); + + void ITextViewConnect.AddToTextView(TextView textView) + { + if (textView != null && !textViews.Contains(textView)) { + Debug.Assert(textView.Document == document); + textViews.Add(textView); + } + } + + void ITextViewConnect.RemoveFromTextView(TextView textView) + { + if (textView != null) { + Debug.Assert(textView.Document == document); + textViews.Remove(textView); + } + } + #endregion + } + + public sealed class TextMarker : TextSegment, ITextMarker + { + readonly TextMarkerService service; + + public TextMarker(TextMarkerService service, int startOffset, int length) + { + if (service == null) + throw new ArgumentNullException("service"); + this.service = service; + this.StartOffset = startOffset; + this.Length = length; + this.markerTypes = TextMarkerTypes.None; + } + + public event EventHandler Deleted; + + public bool IsDeleted { + get { return !this.IsConnectedToCollection; } + } + + public void Delete() + { + service.Remove(this); + } + + internal void OnDeleted() + { + if (Deleted != null) + Deleted(this, EventArgs.Empty); + } + + void Redraw() + { + service.Redraw(this); + } + + Color? backgroundColor; + + public Color? BackgroundColor { + get { return backgroundColor; } + set { + if (backgroundColor != value) { + backgroundColor = value; + Redraw(); + } + } + } + + Color? foregroundColor; + + public Color? ForegroundColor { + get { return foregroundColor; } + set { + if (foregroundColor != value) { + foregroundColor = value; + Redraw(); + } + } + } + + FontWeight? fontWeight; + + public FontWeight? FontWeight { + get { return fontWeight; } + set { + if (fontWeight != value) { + fontWeight = value; + Redraw(); + } + } + } + + FontStyle? fontStyle; + + public FontStyle? FontStyle { + get { return fontStyle; } + set { + if (fontStyle != value) { + fontStyle = value; + Redraw(); + } + } + } + + public object Tag { get; set; } + + TextMarkerTypes markerTypes; + + public TextMarkerTypes MarkerTypes { + get { return markerTypes; } + set { + if (markerTypes != value) { + markerTypes = value; + Redraw(); + } + } + } + + Color markerColor; + + public Color MarkerColor { + get { return markerColor; } + set { + if (markerColor != value) { + markerColor = value; + Redraw(); + } + } + } + + public object ToolTip { get; set; } + } +} diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs index 7b5c38a2b..8cf615a7c 100644 --- a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel.Design; using System.Diagnostics; using System.IO; using System.Linq; @@ -56,6 +57,7 @@ namespace Tango.Scripting.Editors private List<KnownType> _knownTypes; private List<ScriptType> _declaredTypes; private bool _isLoadingTypes; + private TextMarkerService errorMarkerService; private static JsonSerializerSettings _jsonSettings; private static Dictionary<Type, KnownType> _knownTypesCache; private static String KNOWN_TYPES_CACHE_FOLDER; @@ -296,6 +298,10 @@ namespace Tango.Scripting.Editors completionWindow.InsertionRequest += CompletionWindow_InsertionRequest; TextChanged += ScriptEditor_TextChanged; + + errorMarkerService = new TextMarkerService(Document); + TextArea.TextView.BackgroundRenderers.Add(errorMarkerService); + TextArea.TextView.LineTransformers.Add(errorMarkerService); } private void ScriptEditor_KnownTypesAvailable(object sender, EventArgs e) @@ -2423,6 +2429,18 @@ namespace Tango.Scripting.Editors Document.EndUpdate(); } + public void HighlighError(int position, int length) + { + ITextMarker marker = errorMarkerService.Create(position, length); + marker.MarkerTypes = TextMarkerTypes.SquigglyUnderline; + marker.MarkerColor = Colors.Red; + } + + public void ClearErrors() + { + errorMarkerService.RemoveAll(m => true); + } + #endregion } } diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj index 798529616..a70bbf3de 100644 --- a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj @@ -196,6 +196,8 @@ <Compile Include="CodeCompletion\OverloadViewer.cs" /> <Compile Include="Converters\BooleanToVisibilityConverter.cs" /> <Compile Include="Converters\BooleanToVisibilityInversedConverter.cs" /> + <Compile Include="Errors\ITextMarker.cs" /> + <Compile Include="Errors\TextMarkerService.cs" /> <Compile Include="ExtensionMethods.cs" /> <Compile Include="Intellisense\ClassCompletionItemPopup.cs" /> <Compile Include="Intellisense\CompletionItem.cs" /> |
