Added support for editing multiple UIX files at once

This commit is contained in:
Joshua Askharoun
2021-04-05 18:18:56 -05:00
parent 54c01db152
commit 2597080141
7 changed files with 294 additions and 34 deletions
@@ -0,0 +1,32 @@
using Microsoft.Toolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace ZuneUIXTools.ViewModels
{
public class IrisProjectViewModel : ObservableObject
{
private ObservableCollection<UIXDocumentViewModel> _uixDocuments;
public ObservableCollection<UIXDocumentViewModel> UIXDocuments
{
get => _uixDocuments;
set => SetProperty(ref _uixDocuments, value);
}
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
private UIXDocumentViewModel _selectedDocument;
public UIXDocumentViewModel SelectedDocument
{
get => _selectedDocument;
set => SetProperty(ref _selectedDocument, value);
}
}
}
@@ -0,0 +1,41 @@
using Microsoft.Toolkit.Mvvm.ComponentModel;
using System;
using System.IO;
namespace ZuneUIXTools.ViewModels
{
public class UIXDocumentViewModel : ObservableObject
{
public UIXDocumentViewModel(string fileName)
{
FileName = fileName;
Content = File.ReadAllText(fileName);
}
private string _FileName;
public string FileName
{
get => _FileName;
set => SetProperty(ref _FileName, value);
}
private string _content = null;
public string Content
{
get
{
if (FileName != null)
_content = File.ReadAllText(FileName);
return _content;
}
set => SetProperty(ref _content, value);
}
private string _uiRoot;
public string UIRoot
{
get => _uiRoot;
set => SetProperty(ref _uiRoot, value);
}
}
}
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Toolkit.Mvvm" Version="7.0.1" />
</ItemGroup>
</Project>
+11 -1
View File
@@ -11,10 +11,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VSIX", "VSIX", "{0CB0EB34-B
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IrisVSIX", "VSIX\IrisVSIX\IrisVSIX.csproj", "{B4EEF46A-3235-42BD-9540-7AAA03BAC77D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IrisProjTemplate", "VSIX\IrisProjTemplate\IrisProjTemplate.csproj", "{854495B4-B684-47AC-9771-A644BF47B2FF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrisProjTemplate", "VSIX\IrisProjTemplate\IrisProjTemplate.csproj", "{854495B4-B684-47AC-9771-A644BF47B2FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UIXBuildTask", "VSIX\UIXBuildTask\UIXBuildTask.csproj", "{1FF973F0-C293-439C-83F8-D2C02B624C2D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZuneUIXTools.ViewModels", "ZuneUIXTools.ViewModels\ZuneUIXTools.ViewModels.csproj", "{707AA11C-6D10-4CF2-9852-73FC3D33EF02}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +65,14 @@ Global
{1FF973F0-C293-439C-83F8-D2C02B624C2D}.Release|Any CPU.Build.0 = Release|Any CPU
{1FF973F0-C293-439C-83F8-D2C02B624C2D}.Release|x64.ActiveCfg = Release|Any CPU
{1FF973F0-C293-439C-83F8-D2C02B624C2D}.Release|x64.Build.0 = Release|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Debug|x64.ActiveCfg = Debug|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Debug|x64.Build.0 = Debug|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Release|Any CPU.Build.0 = Release|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Release|x64.ActiveCfg = Release|Any CPU
{707AA11C-6D10-4CF2-9852-73FC3D33EF02}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+56 -12
View File
@@ -5,17 +5,51 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ZuneUIXTools"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
xmlns:syncfusionskin="clr-namespace:Syncfusion.SfSkinManager;assembly=Syncfusion.SfSkinManager.WPF"
mc:Ignorable="d"
Title="Zune UIX Tools" Height="800" Width="1000">
Title="Zune UIX Tools" Height="800" Width="1000"
syncfusionskin:SfSkinManager.Theme="{syncfusionskin:SkinManagerExtension ThemeName=VisualStudio2015}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MaxHeight="400"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Ribbon Title="Microsoft Iris IDE">
<syncfusion:MenuAdv>
<syncfusion:MenuItemAdv Header="File">
<syncfusion:MenuItemAdv Header="Open..." Click="Open_Click">
<Image Source="Images/OpenProject_16x.png"/>
</syncfusion:MenuItemAdv>
<syncfusion:MenuItemAdv Header="Save" Click="Save_Click">
<Image Source="Images/Save_16x.png"/>
</syncfusion:MenuItemAdv>
<syncfusion:MenuItemAdv Header="Save As..." Click="SaveAs_Click">
<Image Source="Images/SaveAs_16x.png"/>
</syncfusion:MenuItemAdv>
</syncfusion:MenuItemAdv>
<syncfusion:MenuItemAdv Header="Edit" />
<syncfusion:MenuItemAdv Header="Build">
<syncfusion:MenuItemAdv Header="Build" Click="Build_Click">
<Image Source="Images/BuildSolution_16x.png"/>
</syncfusion:MenuItemAdv>
<syncfusion:MenuItemAdv Header="Build and Run" Click="BuildAndRun_Click">
<Image Source="Images/StartWithoutDebug_16x.png"/>
</syncfusion:MenuItemAdv>
</syncfusion:MenuItemAdv>
</syncfusion:MenuAdv>
<syncfusion:ToolBarAdv Name="ToolBar" Grid.Row="1">
<syncfusion:SplitButton ToolTip="Build and Run" SmallIcon="Images/StartWithoutDebug_16x.png" Label="Build and Run"
Click="BuildAndRun_Click"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="UI Root" VerticalAlignment="Center" Margin="8,0,4,0"/>
<TextBox Name="UIRootBox" ToolTip="The UI element to load and display" MinWidth="150"/>
</StackPanel>
</syncfusion:ToolBarAdv>
<!--<Ribbon Title="Microsoft Iris IDE">
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="Images/ZuneShell.ico" Background="Black" BorderBrush="Black">
<RibbonApplicationMenuItem Header="Open..." ImageSource="Images/OpenProject_16x.png"
@@ -34,20 +68,30 @@
<RibbonTextBox Name="UIRootBox" Label="UI Root" ToolTip="The UI element to load and display"/>
</RibbonGroup>
</RibbonTab>
</Ribbon>
</Ribbon>-->
<avalonEdit:TextEditor Name="textEditor" Grid.Row="1" Padding="4" ShowLineNumbers="True"
SyntaxHighlighting="XML" FontFamily="JetBrains Mono" FontSize="10pt"/>
<syncfusion:DockingManager x:Name="DockingManager" Grid.Row="2" ContainerMode="TDI">
<ContentControl x:Name="SolutionExplorer" syncfusion:DockingManager.Header="Solution Explorer"
syncfusion:DockingManager.State="Dock" syncfusion:DockingManager.SideInDockedMode="Right"/>
<ContentControl x:Name="ToolBox" syncfusion:DockingManager.Header="Toolbox"
syncfusion:DockingManager.State="AutoHidden"/>
<ContentControl x:Name="Properties" syncfusion:DockingManager.Header="Properties"
syncfusion:DockingManager.State="AutoHidden"/>
<ContentControl x:Name="ErrorList" syncfusion:DockingManager.Header="Error List"
syncfusion:DockingManager.State="Dock" syncfusion:DockingManager.SideInDockedMode="Bottom">
<ScrollViewer Grid.Row="3">
<StackPanel Name="ErrorPanel" Margin="4,4,4,0"/>
</ScrollViewer>
</ContentControl>
</syncfusion:DockingManager>
<GridSplitter Grid.Row="2"
<!--<GridSplitter Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Background="LightGray"
ShowsPreview="True"
Height="5" />
Height="5" />-->
<ScrollViewer Grid.Row="3">
<StackPanel Name="ErrorPanel" Margin="4,4,4,0"/>
</ScrollViewer>
</Grid>
</Window>
+131 -19
View File
@@ -1,4 +1,5 @@
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.AvalonEdit.Rendering;
@@ -22,6 +23,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ZuneUIXTools.ViewModels;
using Application = Microsoft.Iris.Application;
using Window = System.Windows.Window;
@@ -33,9 +35,13 @@ namespace ZuneUIXTools
public partial class MainWindow : Window
{
private const string FILE_FORMAT_FILTER = "Microsoft Iris UI (*.uix)|*.uix|XML files (*.xml)|*.xml";
private readonly FontFamily CODE_FONT = new FontFamily("JetBrains Mono");
public string DocumentPath { get; set; }
public string UIRoot { get; set; }
public IrisProjectViewModel IrisProject { get; } = new IrisProjectViewModel
{
Name = "IrisApp1",
UIXDocuments = new System.Collections.ObjectModel.ObservableCollection<UIXDocumentViewModel>()
};
public MainWindow()
{
@@ -44,9 +50,82 @@ namespace ZuneUIXTools
Loaded += MainWindow_Loaded;
}
private void RemoveDocument(string fileName, bool removeFromProject = true, bool removeFromUI = true)
{
if (removeFromProject)
IrisProject.UIXDocuments.Remove(IrisProject.UIXDocuments.First(doc => doc.FileName == fileName));
if (!removeFromUI)
return;
FrameworkElement elem = GetDocumentUI(fileName);
if (elem != null)
DockingManager.Children.Remove(elem);
}
private void AddDocument(string fileName, bool addToProject = true, bool addToUI = true)
{
if (addToProject && !IrisProject.UIXDocuments.Any(doc => doc.FileName == fileName))
IrisProject.UIXDocuments.Add(new UIXDocumentViewModel(fileName));
if (!addToUI)
return;
// <avalonEdit:TextEditor Name="textEditor" Grid.Row="1" Padding="4" ShowLineNumbers="True"
// SyntaxHighlighting="XML" FontFamily="JetBrains Mono" FontSize="10pt" />
var editor = new TextEditor()
{
Tag = fileName,
SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML"),
Padding = new Thickness(4),
ShowLineNumbers = true,
FontFamily = CODE_FONT,
FontSize = 10
};
editor.Load(fileName);
DockingManager.Children.Add(editor);
editor.GotFocus += EditorGotFocus;
Syncfusion.Windows.Tools.Controls.DockingManager.ChangeState(editor, Syncfusion.Windows.Tools.Controls.DockState.Document);
Syncfusion.Windows.Tools.Controls.DockingManager.SetHeader(editor, System.IO.Path.GetFileName(fileName));
}
private FrameworkElement GetDocumentUI(string fileName)
{
FrameworkElement elem = null;
foreach (FrameworkElement candidate in DockingManager.Children)
{
if (candidate.Tag?.ToString() == fileName)
{
elem = candidate;
break;
}
}
return elem;
}
private void SetSelectedDocument(string fileName)
{
IrisProject.SelectedDocument = IrisProject.UIXDocuments.First(doc => doc.FileName == fileName);
}
private void EditorGotFocus(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement elem)
SetSelectedDocument(elem.Tag.ToString());
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
IrisProject.UIXDocuments.CollectionChanged += UIXDocuments_CollectionChanged;
}
private void UIXDocuments_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
foreach (UIXDocumentViewModel doc in e.OldItems)
RemoveDocument(doc.FileName, removeFromProject: false);
if (e.NewItems != null)
foreach (UIXDocumentViewModel doc in e.NewItems)
AddDocument(doc.FileName, addToProject: false);
}
private void BuildAndRun()
@@ -78,11 +157,17 @@ namespace ZuneUIXTools
{
errBlock.MouseDown += (object sender, MouseButtonEventArgs args) =>
{
try
{
string fileName = new Uri(err.Context).LocalPath;
var editor = (TextEditor)GetDocumentUI(fileName);
// Highlight the error in the code editor
int offset = textEditor.Document.GetOffset(err.Line, err.Column);
textEditor.SelectionStart = offset;
textEditor.SelectionLength = 1;
textEditor.ScrollTo(err.Line, err.Column);
int offset = editor.Document.GetOffset(err.Line, err.Column);
editor.SelectionStart = offset;
editor.SelectionLength = 1;
editor.ScrollTo(err.Line, err.Column);
}
catch { }
};
}
@@ -91,7 +176,8 @@ namespace ZuneUIXTools
}
};
string compiledFile = System.IO.Path.ChangeExtension(DocumentPath, "uib");
string sourceFile = IrisProject.SelectedDocument.FileName;
string compiledFile = System.IO.Path.ChangeExtension(sourceFile, "uib");
bool isSuccess = false;
try
{
@@ -100,7 +186,7 @@ namespace ZuneUIXTools
{
new CompilerInput()
{
SourceFileName = DocumentPath,
SourceFileName = sourceFile,
OutputFileName = compiledFile
}
},
@@ -137,8 +223,9 @@ namespace ZuneUIXTools
try
{
string uiRoot = IrisProject.SelectedDocument.UIRoot;
Application.Window.SetBackgroundColor(new WindowColor(0xE6, 0xE6, 0xE6));
Application.Window.RequestLoad("file://" + compiledFile + (string.IsNullOrEmpty(UIRoot) ? string.Empty : "#" + UIRoot));
Application.Window.RequestLoad("file://" + compiledFile + (string.IsNullOrEmpty(uiRoot) ? string.Empty : "#" + uiRoot));
Application.Window.CloseRequested += (object sender, WindowCloseRequestedEventArgs args) =>
{
args.BlockCloseRequest();
@@ -161,13 +248,31 @@ namespace ZuneUIXTools
private void Build_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(DocumentPath))
var doc = IrisProject.SelectedDocument;
if (doc == null)
return;
// Save changes before running
textEditor.Save(DocumentPath);
((TextEditor)GetDocumentUI(doc.FileName)).Save(doc.FileName);
// Set UI root
UIRoot = UIRootBox.Text;
doc.UIRoot = UIRootBox.Text;
var _buildThread = new Thread(new ThreadStart(BuildAndRun));
_buildThread.SetApartmentState(ApartmentState.STA);
_buildThread.IsBackground = true;
_buildThread.Start();
}
private void BuildAndRun_Click(object sender, RoutedEventArgs e)
{
var doc = IrisProject.SelectedDocument;
if (doc == null)
return;
// Save changes before running
((TextEditor)GetDocumentUI(doc.FileName)).Save(doc.FileName);
// Set UI root
doc.UIRoot = UIRootBox.Text;
var _buildThread = new Thread(new ThreadStart(BuildAndRun));
_buildThread.SetApartmentState(ApartmentState.STA);
@@ -184,27 +289,34 @@ namespace ZuneUIXTools
if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
return;
DocumentPath = openFileDialog.FileName;
textEditor.Load(DocumentPath);
AddDocument(openFileDialog.FileName, addToUI: false);
}
private void Save_Click(object sender, RoutedEventArgs e)
{
textEditor.Save(DocumentPath);
var doc = IrisProject.SelectedDocument;
if (doc == null)
return;
var editor = GetDocumentUI(doc.FileName) as TextEditor;
editor.Save(doc.FileName);
}
private void SaveAs_Click(object sender, RoutedEventArgs e)
{
if (IrisProject.SelectedDocument == null)
return;
SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = FILE_FORMAT_FILTER,
FileName = DocumentPath
FileName = IrisProject.SelectedDocument.FileName
};
if (saveFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
return;
DocumentPath = saveFileDialog.FileName;
textEditor.Save(DocumentPath);
var editor = GetDocumentUI(IrisProject.SelectedDocument.FileName) as TextEditor;
editor.Save(saveFileDialog.FileName);
AddDocument(saveFileDialog.FileName);
}
}
}
+9
View File
@@ -14,10 +14,19 @@
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.1.1" />
<PackageReference Include="Syncfusion.SfSkinManager.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Themes.Blend.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Themes.FluentDark.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Themes.MaterialLight.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Themes.Metro.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Themes.Office2019Colorful.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Themes.VisualStudio2015.WPF" Version="19.1.0.54" />
<PackageReference Include="Syncfusion.Tools.WPF" Version="19.1.0.54" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ZuneShell\UIX\UIX.csproj" />
<ProjectReference Include="..\ZuneUIXTools.ViewModels\ZuneUIXTools.ViewModels.csproj" />
</ItemGroup>
<ItemGroup>