diff --git a/.github/workflows/Nuke.yml b/.github/workflows/Nuke.yml index a8610dc39..3e560292c 100644 --- a/.github/workflows/Nuke.yml +++ b/.github/workflows/Nuke.yml @@ -9,8 +9,8 @@ jobs: runs-on: windows-2022 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Run Nuke Build - run: ./.nuke/build.cmd --GitHubToken ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + run: ./.nuke/build.cmd PublishGitHub --GitHubToken ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/Qodana.yml b/.github/workflows/Qodana.yml index 6ba3b0da1..38ce916ad 100644 --- a/.github/workflows/Qodana.yml +++ b/.github/workflows/Qodana.yml @@ -14,13 +14,14 @@ jobs: pull-requests: write checks: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: 'Qodana Scan' - uses: JetBrains/qodana-action@v2023.2 + uses: JetBrains/qodana-action@main with: - args: --linter,jetbrains/qodana-dotnet:2023.3-eap + pr-mode: false + github-token: ${{ secrets.GITHUB_TOKEN }} env: - QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} \ No newline at end of file + QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} diff --git a/.nuke/build.ps1 b/.nuke/build.ps1 index 927e2a3d4..13bd335e4 100644 --- a/.nuke/build.ps1 +++ b/.nuke/build.ps1 @@ -13,7 +13,7 @@ Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference ########################################################################### $SolutionDirectory = Split-Path $PSScriptRoot -Parent -$BuildProjectFile = "$SolutionDirectory\Build\Build.csproj" +$BuildProjectFile = "$SolutionDirectory\build\Build.csproj" $TempDirectory = "$SolutionDirectory\.nuke\temp" $DotNetGlobalFile = "$SolutionDirectory\global.json" diff --git a/Changelog.md b/Changelog.md index 75ca4800e..3bcc5dcc5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,53 @@ # Changelog +# 2023-12-25 **2024.0.11** + +In this release RevitLookup welcomes you with improved visuals, support for templates to fine-tune data display, improved navigation, in-depth color support, let's take a look + +## General + +- **Navigation**. Updated navigation allows `Ctrl + Click` in the tree or grid to open any selected item or group of items in a new tab. + This also allows you to analyze items that RevitLookup doesn't support, how about looking at StackTrace for exceptions + ![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/0c13c6da-772f-453b-9d34-bff609c04d95) + +- **Color Preview**. Changes to the user interface give us the ability to customize the display of any type of data. + And now you will be able to visually see how materials or ribbon looks like. + `Autodesk.Revit.DB.Color` and `System.Windows.Media.Color` are supported + ![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/3b736961-26fa-4a24-b916-bb7c4fddfda9) + +## Improvements + +- **Update available notification**. Updates are now checked automatically and an icon is now displayed in the navigation area if a new version is available + + ![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/b7ab5fd0-b927-4b9a-805c-91e45fbd9f14) + +- **Background effects** Available on windows 11 only. + + Acrylic: + + ![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/259012f7-f19d-4779-8b17-4be96a404023) + + Blur: + + ![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/e8046bf0-ae48-446e-94e3-e3fdc59898e4) + + The visual representation of the background depends on your desktop image and current theme + +- **Color extensions** Convert color to other formats HEX, CMYK, etc. Color name identification, `en` and `ru` localizations available. + `Autodesk.Revit.DB.Color` and `System.Windows.Media.Color` are supported + ![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/668a9c5c-3239-4100-8829-63fc71c880fb) + +## Bugs + +- Fixed incorrect display when switching themes on windows 10 https://github.com/jeremytammik/RevitLookup/issues/194 +- Returned deleted notification when checking for updates + +## Misc + +- Updated developer's [guide](https://github.com/jeremytammik/RevitLookup/blob/dev/Contributing.md#styles). + +Here, I'm wrapping things up. Wishing everyone a splendid New Year and a joyous Christmas ahead. As always, yours truly @Nice3point 🎅 + # 2023-12-01 **2024.0.10** ## General diff --git a/Contributing.md b/Contributing.md index ff7e0ae41..f6755f8b3 100644 --- a/Contributing.md +++ b/Contributing.md @@ -22,8 +22,8 @@ The naming should be descriptive and direct, giving a clear idea of the function ## Prerequisites for Compiling RevitLookup -- .Net 7 SDK or newer -- Visual Studio 2022 / JetBrains Rider 2022.3 or newer +- .Net 8 SDK or newer +- Visual Studio 2022 / JetBrains Rider 2023.3 or newer ## Architecture @@ -160,4 +160,45 @@ public sealed class ElementDescriptor : Descriptor, IDescriptorConnector .AddShortcut(bindableElement, ModifierKeys.Alt, Key.F7); } } -``` \ No newline at end of file +``` + +## Styles + +The application UI is divided into templates, where each template can be customized for different types of data. +There are several different rules for customizing TreeView, DataGrid row, DataGrid cell and they are all located in the file `RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Styles.cs`. + +Suggested methods search for a style/template by name: + +```C# +public override DataTemplate SelectTemplate(object item, DependencyObject container) +{ + if (item is null) return null; + + var descriptor = (Descriptor) item; + var presenter = (FrameworkElement) container; + var templateName = descriptor.Value.Descriptor switch + { + ColorDescriptor => "DataGridColorCellTemplate", + ColorMediaDescriptor => "DataGridColorCellTemplate", + _ => "DefaultLookupDataGridCellTemplate" + }; + + return (DataTemplate) presenter.FindResource(templateName); +} +``` + +The templates themselves are located in the `RevitLookup/Views/Controls` folder. +For example, in the `RevitLookup/Views/Controls/DataGrid/DataGridCellTemplate.xaml` file there is a cell template that displays the text: + +```xaml + + + +``` + +References to additional files must be registered in `RevitLookup/Views/Resources/RevitLookup.Ui.xaml`. \ No newline at end of file diff --git a/RevitLookup.UI/Controls/DynamicScrollViewer/PassiveScrollViewer.xaml b/RevitLookup.UI/Controls/DynamicScrollViewer/PassiveScrollViewer.xaml deleted file mode 100644 index 16ddc979e..000000000 --- a/RevitLookup.UI/Controls/DynamicScrollViewer/PassiveScrollViewer.xaml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SnoopView.xaml b/RevitLookup/Views/Pages/SnoopView.xaml deleted file mode 100644 index 61dcc42fc..000000000 --- a/RevitLookup/Views/Pages/SnoopView.xaml +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SnoopViewBase.xaml.cs b/RevitLookup/Views/Pages/SnoopViewBase.xaml.cs deleted file mode 100644 index 81f49db71..000000000 --- a/RevitLookup/Views/Pages/SnoopViewBase.xaml.cs +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright 2003-2023 by Autodesk, Inc. -// -// Permission to use, copy, modify, and distribute this software in -// object code form for any purpose and without fee is hereby granted, -// provided that the above copyright notice appears in all copies and -// that both that copyright notice and the limited warranty and -// restricted rights notice below appear in all supporting -// documentation. -// -// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. -// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF -// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. -// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE -// UNINTERRUPTED OR ERROR FREE. -// -// Use, duplication, or disclosure by the U.S. Government is subject to -// restrictions set forth in FAR 52.227-19 (Commercial Computer -// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) -// (Rights in Technical Data and Computer Software), as applicable. - -using System.Collections; -using System.ComponentModel; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Input; -using CommunityToolkit.Mvvm.Input; -using RevitLookup.Core.Contracts; -using RevitLookup.Core.Enums; -using RevitLookup.Core.Objects; -using RevitLookup.Services.Contracts; -using RevitLookup.ViewModels.Contracts; -using RevitLookup.Views.Extensions; -using RevitLookup.Views.Utils; -using Wpf.Ui.Controls; -using DataGrid = Wpf.Ui.Controls.DataGrid; -using TreeView = Wpf.Ui.Controls.TreeView; -using TreeViewItem = System.Windows.Controls.TreeViewItem; - -namespace RevitLookup.Views.Pages; - -public class SnoopViewBase : Page, INavigableView, INavigationAware -{ - private readonly ISettingsService _settingsService; - private readonly DataGrid _dataGridControl; - - protected SnoopViewBase(ISettingsService settingsService) - { - _settingsService = settingsService; - AddShortcuts(); - } - - protected UIElement SearchBoxControl { get; init; } - protected TreeView TreeViewControl { get; init; } - - protected DataGrid DataGridControl - { - get => _dataGridControl; - init - { - _dataGridControl = value; - OnDataGridChanged(value); - } - } - - public ISnoopViewModel ViewModel { get; protected init; } - - protected void OnTreeSourceChanged(object sender, IEnumerable enumerable) - { - if (IsLoaded) - { - SetupTreeView(); - return; - } - - Loaded += OnLoaded; - - void OnLoaded(object o, RoutedEventArgs args) - { - Loaded -= OnLoaded; - SetupTreeView(); - } - } - - /// - /// Execute collector for selection - /// - protected void OnTreeSelectionChanged(object sender, RoutedPropertyChangedEventArgs e) - { - switch (e.NewValue) - { - case SnoopableObject snoopableObject: - ViewModel.SelectedObject = snoopableObject; - break; - case CollectionViewGroup: - ViewModel.SelectedObject = null; - break; - default: - //Internal __Canon object - return; - } - - ViewModel.FetchMembersCommand.Execute(null); - } - - private async void OnDataGridChanged(DataGrid control) - { - //Lazy init. 1 ms is enough to display data and start initialising components - await Task.Delay(1); - - ValidateTimeColumn(control); - CreateGridContextMenu(control); - control.ItemsSourceChanged += OnGridItemsSourceChanged; - } - - private void OnGridItemsSourceChanged(object sender, EventArgs _) - { - var dataGrid = (DataGrid) sender; - - //Clear shapingStorage for remove duplications. WpfBug? - dataGrid.Items.GroupDescriptions!.Clear(); - dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Depth), ListSortDirection.Descending)); - dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.MemberAttributes), ListSortDirection.Ascending)); - dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Name), ListSortDirection.Ascending)); - dataGrid.Items.GroupDescriptions.Add(new PropertyGroupDescription(nameof(Descriptor.Type))); - } - - /// - /// Navigate selection in new window - /// - protected void OnGridMouseLeftButtonUp(object sender, RoutedEventArgs routedEventArgs) - { - if (DataGridControl.SelectedItems.Count != 1) return; - ViewModel.Navigate((Descriptor) DataGridControl.SelectedItem); - } - - /// - /// Create tooltip, menu - /// - protected async void OnRowLoaded(object sender, RoutedEventArgs routedEventArgs) - { - //Lazy init. 1 ms is enough to display data and start initialising components - await Task.Delay(1); - - var element = (FrameworkElement) sender; - Descriptor descriptor; - switch (element.DataContext) - { - case SnoopableObject context: - descriptor = context.Descriptor; - var treeItem = VisualUtils.FindVisualParent((DependencyObject) sender); - CreateTreeTooltip(descriptor, treeItem); - CreateTreeContextMenu(descriptor, treeItem); - break; - case Descriptor context: - descriptor = context; - CreateGridRowTooltip(descriptor, element); - CreateGridRowContextMenu(descriptor, element); - break; - default: - return; - } - } - - public void OnNavigatedTo() - { - Wpf.Ui.Application.MainWindow.PreviewKeyDown += OnKeyPressed; - } - - public void OnNavigatedFrom() - { - Wpf.Ui.Application.MainWindow.PreviewKeyDown -= OnKeyPressed; - } - - private void OnKeyPressed(object sender, KeyEventArgs e) - { - if (SearchBoxControl.IsKeyboardFocused) return; - if (e.KeyboardDevice.Modifiers != ModifierKeys.None) return; - if (e.Key is >= Key.D0 and <= Key.Z or >= Key.NumPad0 and <= Key.NumPad9) SearchBoxControl.Focus(); - } - - private void CreateTreeTooltip(Descriptor descriptor, FrameworkElement row) - { - row.ToolTip = new ToolTip - { - Content = new StringBuilder() - .Append("Type: ") - .AppendLine(descriptor.Type) - .Append("Value: ") - .Append(descriptor.Name) - .ToString() - }; - } - - private void CreateGridRowTooltip(Descriptor descriptor, FrameworkElement row) - { - var builder = new StringBuilder(); - - if ((descriptor.MemberAttributes & MemberAttributes.Private) != 0) builder.Append("Private "); - if ((descriptor.MemberAttributes & MemberAttributes.Static) != 0) builder.Append("Static "); - if ((descriptor.MemberAttributes & MemberAttributes.Property) != 0) builder.Append("Property: "); - if ((descriptor.MemberAttributes & MemberAttributes.Extension) != 0) builder.Append("Extension: "); - if ((descriptor.MemberAttributes & MemberAttributes.Method) != 0) builder.Append("Method: "); - if ((descriptor.MemberAttributes & MemberAttributes.Event) != 0) builder.Append("Event: "); - if ((descriptor.MemberAttributes & MemberAttributes.Field) != 0) builder.Append("Field: "); - - builder.AppendLine(descriptor.Name) - .Append("Type: ") - .AppendLine(descriptor.Value.Descriptor.Type) - .Append("Value: ") - .Append(descriptor.Value.Descriptor.Name); - - if (descriptor.Value.Descriptor.Description is not null) - builder.AppendLine() - .Append("Description: ") - .Append(descriptor.Value.Descriptor.Description); - - if (descriptor.ComputationTime > 0) - builder.AppendLine() - .Append("Time: ") - .Append(descriptor.ComputationTime) - .Append(" ms"); - - row.ToolTip = new ToolTip - { - Content = builder.ToString() - }; - } - - private void CreateTreeContextMenu(Descriptor descriptor, FrameworkElement row) - { - var contextMenu = new ContextMenu - { - Resources = Resources - }; - - contextMenu.AddMenuItem("CopyMenuItem") - .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Name)) - .SetShortcut(row, ModifierKeys.Control, Key.C); - contextMenu.AddMenuItem("HelpMenuItem") - .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName)) - .SetShortcut(row, Key.F1); - - if (descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); - row.ContextMenu = contextMenu; - } - - private void CreateGridContextMenu(DataGrid dataGrid) - { - var contextMenu = new ContextMenu - { - Resources = Resources - }; - - contextMenu.AddMenuItem("RefreshMenuItem") - .SetCommand(ViewModel.RefreshMembersCommand) - .SetGestureText(Key.F5); - - contextMenu.AddSeparator(); - contextMenu.AddLabel("Columns"); - - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Time") - .SetCommand(dataGrid.Columns[2].Visibility == Visibility.Visible, parameter => - { - dataGrid.Columns[2].Visibility = parameter ? Visibility.Collapsed : Visibility.Visible; - _settingsService.ShowTimeColumn = !parameter; - }); - - contextMenu.AddSeparator(); - contextMenu.AddLabel("Show"); - - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Events") - .SetCommand(_settingsService.IncludeEvents, parameter => - { - _settingsService.IncludeEvents = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Extensions") - .SetCommand(_settingsService.IncludeExtensions, parameter => - { - _settingsService.IncludeExtensions = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Fields") - .SetCommand(_settingsService.IncludeFields, parameter => - { - _settingsService.IncludeFields = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Non-public") - .SetCommand(_settingsService.IncludePrivate, parameter => - { - _settingsService.IncludePrivate = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Root hierarchy") - .SetCommand(_settingsService.IncludeRootHierarchy, parameter => - { - _settingsService.IncludeRootHierarchy = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Static") - .SetCommand(_settingsService.IncludeStatic, parameter => - { - _settingsService.IncludeStatic = !parameter; - return GetRefreshGridTask(); - }); - contextMenu.AddMenuItem("CheckableMenuItem") - .SetHeader("Unsupported") - .SetCommand(_settingsService.IncludeUnsupported, parameter => - { - _settingsService.IncludeUnsupported = !parameter; - return GetRefreshGridTask(); - }); - - dataGrid.ContextMenu = contextMenu; - } - - private void CreateGridRowContextMenu(Descriptor descriptor, FrameworkElement row) - { - var contextMenu = new ContextMenu - { - Resources = Resources - }; - - contextMenu.AddMenuItem("CopyMenuItem") - .SetCommand(descriptor, parameter => Clipboard.SetText($"{parameter.Name}: {parameter.Value.Descriptor.Name}")) - .SetShortcut(row, ModifierKeys.Control, Key.C); - - contextMenu.AddMenuItem("CopyMenuItem") - .SetHeader("Copy value") - .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Value.Descriptor.Name)) - .SetShortcut(row, ModifierKeys.Control | ModifierKeys.Shift, Key.C) - .SetAvailability(descriptor.Value.Descriptor.Name is not null); - - contextMenu.AddMenuItem("HelpMenuItem") - .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName, parameter.Name)) - .SetShortcut(row, new KeyGesture(Key.F1)); - - if (descriptor.Value.Descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); - row.ContextMenu = contextMenu; - } - - private Task GetRefreshGridTask() - { - return ViewModel.RefreshMembersCommand.ExecuteAsync(null); - } - - private async void SetupTreeView() - { - // Await Frame transition. GetMembers freezes the thread and breaks the animation - await Task.Delay(_settingsService.TransitionDuration); - - // if (TryRestoreSelection()) return; - - ExpandFirstGroup(); - } - - [Obsolete("Low performance")] - private bool TryRestoreSelection() - { - if (ViewModel.SelectedObject is null) return false; - - var treeViewItem = VisualUtils.GetTreeViewItem(TreeViewControl, ViewModel.SelectedObject); - if (treeViewItem is null || treeViewItem.IsSelected) return false; - - TreeViewControl.SelectedItemChanged -= OnTreeSelectionChanged; - treeViewItem.IsSelected = true; - TreeViewControl.SelectedItemChanged += OnTreeSelectionChanged; - return true; - } - - private void ExpandFirstGroup() - { - if (TreeViewControl.Items.Count > 3) return; - - var rootItem = VisualUtils.GetTreeViewItem(TreeViewControl, 0); - if (rootItem is null) return; - - var nestedItem = VisualUtils.GetTreeViewItem(rootItem, 0); - if (nestedItem is null) return; - - nestedItem.IsSelected = true; - } - - private void ValidateTimeColumn(System.Windows.Controls.DataGrid control) - { - control.Columns[2].Visibility = _settingsService.ShowTimeColumn ? Visibility.Visible : Visibility.Collapsed; - } - - private void AddShortcuts() - { - var command = new AsyncRelayCommand(() => ViewModel.RefreshMembersCommand.ExecuteAsync(null)); - InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.F5))); - } -} \ No newline at end of file diff --git a/RevitLookup/Views/Resources/Menus.xaml b/RevitLookup/Views/Resources/Menus.xaml deleted file mode 100644 index 1e730dfef..000000000 --- a/RevitLookup/Views/Resources/Menus.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RevitLookup/app.manifest b/RevitLookup/app.manifest deleted file mode 100644 index b51e44ba9..000000000 --- a/RevitLookup/app.manifest +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PerMonitor - true/PM - true - - - - - - - - - - - \ No newline at end of file diff --git a/Build/.editorconfig b/build/.editorconfig similarity index 100% rename from Build/.editorconfig rename to build/.editorconfig diff --git a/Build/Build.CI.GitHub.cs b/build/Build.CI.GitHub.cs similarity index 53% rename from Build/Build.CI.GitHub.cs rename to build/Build.CI.GitHub.cs index caf3f935f..58ac5470a 100644 --- a/Build/Build.CI.GitHub.cs +++ b/build/Build.CI.GitHub.cs @@ -2,14 +2,15 @@ using System.Text.RegularExpressions; using Nuke.Common; using Nuke.Common.Git; +using Nuke.Common.Tools.Git; using Nuke.Common.Tools.GitHub; using Octokit; using Serilog; sealed partial class Build { - Target Publish => _ => _ - .TriggeredBy(CreateInstaller) + Target PublishGitHub => _ => _ + .DependsOn(CreateInstaller) .Requires(() => GitHubToken) .OnlyWhenStatic(() => IsServerBuild && GitRepository.IsOnMainOrMasterBranch()) .Executes(async () => @@ -22,13 +23,12 @@ sealed partial class Build var version = VersionMap.Values.Last(); var gitHubName = GitRepository.GetGitHubName(); var gitHubOwner = GitRepository.GetGitHubOwner(); - var artifacts = Directory.GetFiles(ArtifactsDirectory, "*"); - var tags = await GitHubTasks.GitHubClient.Repository.GetAllTags(gitHubOwner, gitHubName); - Assert.False(tags.Select(tag => tag.Name).Contains(version), $"A Release with the specified tag already exists in the repository: {version}"); - Log.Information("Version: {Version}", version); + Validate(version); + + var artifacts = Directory.GetFiles(ArtifactsDirectory, "*"); + var changelog = CreateChangelog(version); - var changelog = CreateChangelog(tags[^1].Name, version); var newRelease = new NewRelease(version) { Name = version, @@ -36,12 +36,17 @@ sealed partial class Build TargetCommitish = GitRepository.Commit }; - var release = await CreatedReleaseAsync(gitHubOwner, gitHubName, newRelease); + var release = await GitHubTasks.GitHubClient.Repository.Release.Create(gitHubOwner, gitHubName, newRelease); await UploadArtifactsAsync(release, artifacts); }); - static Task CreatedReleaseAsync(string gitHubOwner, string gitHubName, NewRelease newRelease) => - GitHubTasks.GitHubClient.Repository.Release.Create(gitHubOwner, gitHubName, newRelease); + static void Validate(string version) + { + var tags = GitTasks.Git("tag --list"); + Assert.False(tags.Any(tag => tag.Text == version), $"A Release with the specified tag already exists in the repository: {version}"); + + Log.Information("Version: {Version}", version); + } static async Task UploadArtifactsAsync(Release createdRelease, IEnumerable artifacts) { @@ -59,7 +64,7 @@ static async Task UploadArtifactsAsync(Release createdRelease, IEnumerable 0) + if (hasEntry) { - if (line.StartsWith(nextRecordSymbol)) break; - logBuilder.AppendLine(line); + if (line.StartsWith(separator)) break; + if (line == string.Empty) continue; + + if (changelog.Length > 0) changelog.AppendLine(); + changelog.Append(line); continue; } - if (!changelogLineRegex.Match(line).Success) continue; - var truncatedLine = changelogLineRegex.Replace(line, string.Empty); - logBuilder.AppendLine(truncatedLine); + if (line.StartsWith(separator) && line.Contains(version)) + { + hasEntry = true; + } } - if (logBuilder.Length == 0) Log.Warning("No version entry exists in the changelog: {Version}", version); - - AppendCompareUrl(logBuilder, previousVersion, version); - return logBuilder.ToString(); - } - - void AppendCompareUrl(StringBuilder logBuilder, string previousVersion, string version) - { - logBuilder.Append("Full changelog: "); - logBuilder.Append(GitRepository.GetGitHubCompareTagsUrl(previousVersion, version)); + return changelog; } } \ No newline at end of file diff --git a/Build/Build.Clean.cs b/build/Build.Clean.cs similarity index 100% rename from Build/Build.Clean.cs rename to build/Build.Clean.cs diff --git a/Build/Build.Compile.cs b/build/Build.Compile.cs similarity index 97% rename from Build/Build.Compile.cs rename to build/Build.Compile.cs index 022d4ce08..7d2a13f45 100644 --- a/Build/Build.Compile.cs +++ b/build/Build.Compile.cs @@ -6,7 +6,7 @@ sealed partial class Build { Target Compile => _ => _ - .TriggeredBy(Clean) + .DependsOn(Clean) .Executes(() => { foreach (var configuration in GlobBuildConfigurations()) diff --git a/Build/Build.Configuration.cs b/build/Build.Configuration.cs similarity index 68% rename from Build/Build.Configuration.cs rename to build/Build.Configuration.cs index bb524e6e0..3abd68bd3 100644 --- a/Build/Build.Configuration.cs +++ b/build/Build.Configuration.cs @@ -7,11 +7,11 @@ sealed partial class Build protected override void OnBuildInitialized() { - Configurations = new[] - { + Configurations = + [ "Release*", "Installer*" - }; + ]; InstallersMap = new() { @@ -20,10 +20,10 @@ protected override void OnBuildInitialized() VersionMap = new() { - {"Release R21", "2021.2.10"}, - {"Release R22", "2022.2.10"}, - {"Release R23", "2023.2.10"}, - {"Release R24", "2024.0.10"} + {"Release R21", "2021.2.11"}, + {"Release R22", "2022.2.11"}, + {"Release R23", "2023.2.11"}, + {"Release R24", "2024.0.11"} }; } } \ No newline at end of file diff --git a/Build/Build.Installer.cs b/build/Build.Installer.cs similarity index 71% rename from Build/Build.Installer.cs rename to build/Build.Installer.cs index 06d8e5a6e..6ce696cb6 100644 --- a/Build/Build.Installer.cs +++ b/build/Build.Installer.cs @@ -9,7 +9,7 @@ sealed partial class Build { Target CreateInstaller => _ => _ - .TriggeredBy(Compile) + .DependsOn(Compile) .OnlyWhenStatic(() => IsLocalBuild || GitRepository.IsOnMasterBranch()) .Executes(() => { @@ -25,18 +25,18 @@ sealed partial class Build foreach (var directory in directories) { - var proc = new Process(); - proc.StartInfo.FileName = exeFile; - proc.StartInfo.Arguments = directory.DoubleQuoteIfNeeded(); - proc.StartInfo.RedirectStandardOutput = true; - proc.StartInfo.RedirectStandardError = true; - proc.Start(); + var process = new Process(); + process.StartInfo.FileName = exeFile; + process.StartInfo.Arguments = directory.DoubleQuoteIfNeeded(); + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.Start(); - RedirectStream(proc.StandardOutput, LogEventLevel.Information); - RedirectStream(proc.StandardError, LogEventLevel.Error); + RedirectStream(process.StandardOutput, LogEventLevel.Information); + RedirectStream(process.StandardError, LogEventLevel.Error); - proc.WaitForExit(); - if (proc.ExitCode != 0) throw new Exception($"The installer creation failed with ExitCode {proc.ExitCode}"); + process.WaitForExit(); + if (process.ExitCode != 0) throw new Exception($"The installer creation failed with ExitCode {process.ExitCode}"); } } }); diff --git a/Build/Build.Regex.cs b/build/Build.Regex.cs similarity index 100% rename from Build/Build.Regex.cs rename to build/Build.Regex.cs diff --git a/Build/Build.cs b/build/Build.cs similarity index 57% rename from Build/Build.cs rename to build/Build.cs index 881d62c85..d0aab9f00 100644 --- a/Build/Build.cs +++ b/build/Build.cs @@ -1,19 +1,16 @@ using Nuke.Common; using Nuke.Common.Git; using Nuke.Common.ProjectModel; -using Nuke.Common.Tools.GitVersion; sealed partial class Build : NukeBuild { string[] Configurations; - GitRepository GitRepositoryCache; Dictionary VersionMap; Dictionary InstallersMap; [Parameter] string GitHubToken; + [GitRepository] readonly GitRepository GitRepository; [Solution(GenerateProjects = true)] Solution Solution; - GitRepository GitRepository => GitRepositoryCache ??= GitRepository.FromLocalDirectory(RootDirectory); - - public static int Main() => Execute(x => x.Clean); + public static int Main() => Execute(x => x.Compile); } \ No newline at end of file diff --git a/Build/Build.csproj b/build/Build.csproj similarity index 93% rename from Build/Build.csproj rename to build/Build.csproj index f0716cfe7..cdff83639 100644 --- a/Build/Build.csproj +++ b/build/Build.csproj @@ -3,6 +3,7 @@ Exe CS0649;CS0169 + latest true net7.0 .. diff --git a/Build/Build.csproj.DotSettings b/build/Build.csproj.DotSettings similarity index 100% rename from Build/Build.csproj.DotSettings rename to build/Build.csproj.DotSettings diff --git a/Installer/Installer.Generator.cs b/install/Installer.Generator.cs similarity index 100% rename from Installer/Installer.Generator.cs rename to install/Installer.Generator.cs diff --git a/Installer/Installer.Tools.cs b/install/Installer.Tools.cs similarity index 100% rename from Installer/Installer.Tools.cs rename to install/Installer.Tools.cs diff --git a/Installer/Installer.cs b/install/Installer.cs similarity index 100% rename from Installer/Installer.cs rename to install/Installer.cs diff --git a/Installer/Installer.csproj b/install/Installer.csproj similarity index 100% rename from Installer/Installer.csproj rename to install/Installer.csproj diff --git a/Installer/Resources/Icons/BackgroundImage.png b/install/Resources/Icons/BackgroundImage.png similarity index 100% rename from Installer/Resources/Icons/BackgroundImage.png rename to install/Resources/Icons/BackgroundImage.png diff --git a/Installer/Resources/Icons/BannerImage.png b/install/Resources/Icons/BannerImage.png similarity index 100% rename from Installer/Resources/Icons/BannerImage.png rename to install/Resources/Icons/BannerImage.png diff --git a/Installer/Resources/Icons/ShellIcon.ico b/install/Resources/Icons/ShellIcon.ico similarity index 100% rename from Installer/Resources/Icons/ShellIcon.ico rename to install/Resources/Icons/ShellIcon.ico diff --git a/Benchmarks/Benchmark.cs b/source/Benchmarks/Benchmark.cs similarity index 100% rename from Benchmarks/Benchmark.cs rename to source/Benchmarks/Benchmark.cs diff --git a/Benchmarks/Benchmarks.csproj b/source/Benchmarks/Benchmarks.csproj similarity index 100% rename from Benchmarks/Benchmarks.csproj rename to source/Benchmarks/Benchmarks.csproj diff --git a/Benchmarks/ClosureBenchmark.cs b/source/Benchmarks/ClosureBenchmark.cs similarity index 100% rename from Benchmarks/ClosureBenchmark.cs rename to source/Benchmarks/ClosureBenchmark.cs diff --git a/Benchmarks/ResolveTypeBench.cs b/source/Benchmarks/ResolveTypeBench.cs similarity index 100% rename from Benchmarks/ResolveTypeBench.cs rename to source/Benchmarks/ResolveTypeBench.cs diff --git a/Benchmarks/SortBench.cs b/source/Benchmarks/SortBench.cs similarity index 100% rename from Benchmarks/SortBench.cs rename to source/Benchmarks/SortBench.cs diff --git a/Benchmarks/TypeEqualBench.cs b/source/Benchmarks/TypeEqualBench.cs similarity index 100% rename from Benchmarks/TypeEqualBench.cs rename to source/Benchmarks/TypeEqualBench.cs diff --git a/RevitLookup.UI.Demo/App.xaml b/source/RevitLookup.UI.Demo/App.xaml similarity index 100% rename from RevitLookup.UI.Demo/App.xaml rename to source/RevitLookup.UI.Demo/App.xaml diff --git a/RevitLookup.UI.Demo/App.xaml.cs b/source/RevitLookup.UI.Demo/App.xaml.cs similarity index 100% rename from RevitLookup.UI.Demo/App.xaml.cs rename to source/RevitLookup.UI.Demo/App.xaml.cs diff --git a/RevitLookup.UI.Demo/HostProvider.cs b/source/RevitLookup.UI.Demo/HostProvider.cs similarity index 97% rename from RevitLookup.UI.Demo/HostProvider.cs rename to source/RevitLookup.UI.Demo/HostProvider.cs index d3548f736..7286e1f4f 100644 --- a/RevitLookup.UI.Demo/HostProvider.cs +++ b/source/RevitLookup.UI.Demo/HostProvider.cs @@ -7,14 +7,14 @@ using Microsoft.Extensions.Hosting; using RevitLookup.Services; using RevitLookup.Services.Contracts; -using RevitLookup.UI.Demo.Services; +using RevitLookup.UI.Demo.Mock; using RevitLookup.Utils; using RevitLookup.ViewModels.Contracts; using RevitLookup.ViewModels.Pages; using RevitLookup.Views; using RevitLookup.Views.Pages; using Wpf.Ui; -using MoqSnoopViewModel = RevitLookup.UI.Demo.ViewModels.MoqSnoopViewModel; +using MoqSnoopViewModel = RevitLookup.UI.Demo.Mock.MoqSnoopViewModel; namespace RevitLookup.UI.Demo; diff --git a/RevitLookup.UI.Demo/Services/MoqLookupService.cs b/source/RevitLookup.UI.Demo/Mock/MockLookupService.cs similarity index 70% rename from RevitLookup.UI.Demo/Services/MoqLookupService.cs rename to source/RevitLookup.UI.Demo/Mock/MockLookupService.cs index 57277394a..badf46154 100644 --- a/RevitLookup.UI.Demo/Services/MoqLookupService.cs +++ b/source/RevitLookup.UI.Demo/Mock/MockLookupService.cs @@ -26,7 +26,7 @@ using RevitLookup.Services.Enums; using Wpf.Ui; -namespace RevitLookup.UI.Demo.Services; +namespace RevitLookup.UI.Demo.Mock; public sealed class MoqLookupService(IServiceScopeFactory scopeFactory) : ILookupService { @@ -45,7 +45,13 @@ public ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject) _serviceProvider.GetService()!.Snoop(snoopableObject); return this; } - + + public ILookupServiceDependsStage Snoop(IReadOnlyCollection snoopableObjects) + { + _serviceProvider.GetService()!.Snoop(snoopableObjects); + return this; + } + public ILookupServiceShowStage DependsOn(IServiceProvider provider) { _owner = (Window) provider.GetService(); @@ -56,51 +62,50 @@ public ILookupServiceExecuteStage Show() where T : Page { if (_activeTask is null) { - ShowPage(); + ShowPage(); } else { - _activeTask = _activeTask.ContinueWith(_ => ShowPage(), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask = _activeTask.ContinueWith(_ => ShowPage(), TaskScheduler.FromCurrentSynchronizationContext()); } return this; - - void ShowPage() - { - var window = (Window) _serviceProvider.GetService(); - - if (_owner is null) - { - window.WindowStartupLocation = WindowStartupLocation.CenterScreen; - } - else - { - window.Left = _owner.Left + 47; - window.Top = _owner.Top + 49; - } - - window.Show(); - _serviceProvider.GetService().Navigate(typeof(T)); - } } public void Execute(Action handler) where T : class { if (_activeTask is null) { - InvokeHandler(); + InvokeHandler(handler); } else { - _activeTask = _activeTask.ContinueWith(_ => InvokeHandler(), TaskScheduler.FromCurrentSynchronizationContext()); + _activeTask = _activeTask.ContinueWith(_ => InvokeHandler(handler), TaskScheduler.FromCurrentSynchronizationContext()); } - return; + } + + private void ShowPage() where T : Page + { + var window = (Window) _serviceProvider.GetService(); - void InvokeHandler() + if (_owner is null) + { + window.WindowStartupLocation = WindowStartupLocation.CenterScreen; + } + else { - var service = _serviceProvider.GetService(); - handler.Invoke(service); + window.Left = _owner.Left + 47; + window.Top = _owner.Top + 49; } + + window.Show(); + _serviceProvider.GetService().Navigate(typeof(T)); + } + + private void InvokeHandler(Action handler) where T : class + { + var service = _serviceProvider.GetService(); + handler.Invoke(service); } } \ No newline at end of file diff --git a/RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs b/source/RevitLookup.UI.Demo/Mock/MockSnoopViewModel.cs similarity index 91% rename from RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs rename to source/RevitLookup.UI.Demo/Mock/MockSnoopViewModel.cs index a1901a43f..ec28ff316 100644 --- a/RevitLookup.UI.Demo/ViewModels/MoqSnoopViewModel.cs +++ b/source/RevitLookup.UI.Demo/Mock/MockSnoopViewModel.cs @@ -20,7 +20,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using RevitLookup.Core.Contracts; using RevitLookup.Core.Objects; using RevitLookup.Services; using RevitLookup.Services.Contracts; @@ -29,7 +28,7 @@ using RevitLookup.ViewModels.Utils; using RevitLookup.Views.Pages; -namespace RevitLookup.UI.Demo.ViewModels; +namespace RevitLookup.UI.Demo.Mock; public sealed partial class MoqSnoopViewModel(NotificationService notificationService, IServiceProvider provider) : ObservableObject, ISnoopViewModel { @@ -41,13 +40,18 @@ public sealed partial class MoqSnoopViewModel(NotificationService notificationSe public SnoopableObject SelectedObject { get; set; } - public void Navigate(Descriptor selectedItem) + public void Navigate(SnoopableObject selectedItem) { - if (selectedItem.Value.Descriptor is not IDescriptorCollector) return; - if (selectedItem.Value.Descriptor is IDescriptorEnumerator {IsEmpty: true}) return; + Host.GetService() + .Snoop(selectedItem) + .DependsOn(provider) + .Show(); + } + public void Navigate(IReadOnlyCollection selectedItems) + { Host.GetService() - .Snoop(selectedItem.Value) + .Snoop(selectedItems) .DependsOn(provider) .Show(); } diff --git a/RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs b/source/RevitLookup.UI.Demo/Mock/MockSnoopVisualService.cs similarity index 90% rename from RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs rename to source/RevitLookup.UI.Demo/Mock/MockSnoopVisualService.cs index d5d068bd7..852c2e1ff 100644 --- a/RevitLookup.UI.Demo/Services/MoqSnoopVisualService.cs +++ b/source/RevitLookup.UI.Demo/Mock/MockSnoopVisualService.cs @@ -18,7 +18,6 @@ // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. -using System.Diagnostics; using Autodesk.Revit.DB; using Bogus; using RevitLookup.Core.Contracts; @@ -30,7 +29,7 @@ using RevitLookup.ViewModels.Contracts; using Visibility = System.Windows.Visibility; -namespace RevitLookup.UI.Demo.Services; +namespace RevitLookup.UI.Demo.Mock; public sealed class MoqSnoopVisualService(NotificationService notificationService, ISnoopViewModel viewModel, IWindow window) : ISnoopVisualService { @@ -46,6 +45,8 @@ public void Snoop(SnoopableObject snoopableObject) { viewModel.SnoopableObjects = new[] {snoopableObject}; } + + viewModel.SnoopableData = Array.Empty(); } catch (Exception exception) { @@ -53,6 +54,12 @@ public void Snoop(SnoopableObject snoopableObject) } } + public void Snoop(IReadOnlyCollection snoopableObjects) + { + viewModel.SnoopableObjects = snoopableObjects; + viewModel.SnoopableData = Array.Empty(); + } + public async Task SnoopAsync(SnoopableType snoopableType) { switch (snoopableType) @@ -89,8 +96,8 @@ public async Task SnoopAsync(SnoopableType snoopableType) _ => throw new ArgumentOutOfRangeException(nameof(snoopableType), snoopableType, null) }; - viewModel.SnoopableObjects = await GenerateObjectsAsync(generationCount); - viewModel.SnoopableData = Array.Empty(); + var items = await GenerateObjectsAsync(generationCount); + Snoop(items); UpdateWindowVisibility(Visibility.Visible); } @@ -108,12 +115,12 @@ private static async Task> GenerateObjectsA return await Task.Run(() => new Faker() .CustomInstantiator(faker => { - if (faker.IndexFaker % 2000 == 0) return new SnoopableObject(null); + if (faker.IndexFaker % 2000 == 0) return new SnoopableObject((object) null); if (faker.IndexFaker % 1000 == 0) return new SnoopableObject(string.Empty); if (faker.IndexFaker % 700 == 0) return new SnoopableObject(faker.Make(150, () => faker.Internet.UserName())); - if (faker.IndexFaker % 500 == 0) return new SnoopableObject(typeof(Debug)); + if (faker.IndexFaker % 500 == 0) return new SnoopableObject(typeof(DateTime)); if (faker.IndexFaker % 200 == 0) return new SnoopableObject(faker.Lorem.Sentence()); - if (faker.IndexFaker % 100 == 0) return new SnoopableObject(new Color(faker.Random.Byte(), faker.Random.Byte(), faker.Random.Byte())); + if (faker.IndexFaker % 100 == 0) return new SnoopableObject(faker.Make(150, ()=> new Color(faker.Random.Byte(), faker.Random.Byte(), faker.Random.Byte()))); if (faker.IndexFaker % 5 == 0) return new SnoopableObject(faker.Random.Int(0)); if (faker.IndexFaker % 3 == 0) return new SnoopableObject(faker.Random.Bool()); diff --git a/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj b/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj similarity index 100% rename from RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj rename to source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj diff --git a/RevitLookup.UI.Demo/app.manifest b/source/RevitLookup.UI.Demo/app.manifest similarity index 100% rename from RevitLookup.UI.Demo/app.manifest rename to source/RevitLookup.UI.Demo/app.manifest diff --git a/RevitLookup.UI/Animations/Transition.cs b/source/RevitLookup.UI/Animations/Transition.cs similarity index 100% rename from RevitLookup.UI/Animations/Transition.cs rename to source/RevitLookup.UI/Animations/Transition.cs diff --git a/RevitLookup.UI/Animations/TransitionAnimationProvider.cs b/source/RevitLookup.UI/Animations/TransitionAnimationProvider.cs similarity index 100% rename from RevitLookup.UI/Animations/TransitionAnimationProvider.cs rename to source/RevitLookup.UI/Animations/TransitionAnimationProvider.cs diff --git a/RevitLookup.UI/Appearance/ApplicationAccentColorManager.cs b/source/RevitLookup.UI/Appearance/ApplicationAccentColorManager.cs similarity index 98% rename from RevitLookup.UI/Appearance/ApplicationAccentColorManager.cs rename to source/RevitLookup.UI/Appearance/ApplicationAccentColorManager.cs index db1b94d7f..065dd8505 100644 --- a/RevitLookup.UI/Appearance/ApplicationAccentColorManager.cs +++ b/source/RevitLookup.UI/Appearance/ApplicationAccentColorManager.cs @@ -193,8 +193,7 @@ public static void ApplySystemAccent() /// public static Color GetColorizationColor() { - // Windows 10 Dwmapi.dll file not found exception - return Win32.Utilities.IsOSWindows11OrNewer ? UnsafeNativeMethods.GetDwmColor() : Color.FromArgb(255, 0, 120, 212); + return UnsafeNativeMethods.GetDwmColor(); } /// diff --git a/RevitLookup.UI/Appearance/ApplicationTheme.cs b/source/RevitLookup.UI/Appearance/ApplicationTheme.cs similarity index 100% rename from RevitLookup.UI/Appearance/ApplicationTheme.cs rename to source/RevitLookup.UI/Appearance/ApplicationTheme.cs diff --git a/RevitLookup.UI/Appearance/ApplicationThemeManager.cs b/source/RevitLookup.UI/Appearance/ApplicationThemeManager.cs similarity index 100% rename from RevitLookup.UI/Appearance/ApplicationThemeManager.cs rename to source/RevitLookup.UI/Appearance/ApplicationThemeManager.cs diff --git a/RevitLookup.UI/Appearance/ObservedWindow.cs b/source/RevitLookup.UI/Appearance/ObservedWindow.cs similarity index 100% rename from RevitLookup.UI/Appearance/ObservedWindow.cs rename to source/RevitLookup.UI/Appearance/ObservedWindow.cs diff --git a/RevitLookup.UI/Appearance/ResourceDictionaryManager.cs b/source/RevitLookup.UI/Appearance/ResourceDictionaryManager.cs similarity index 100% rename from RevitLookup.UI/Appearance/ResourceDictionaryManager.cs rename to source/RevitLookup.UI/Appearance/ResourceDictionaryManager.cs diff --git a/RevitLookup.UI/Appearance/SystemTheme.cs b/source/RevitLookup.UI/Appearance/SystemTheme.cs similarity index 100% rename from RevitLookup.UI/Appearance/SystemTheme.cs rename to source/RevitLookup.UI/Appearance/SystemTheme.cs diff --git a/RevitLookup.UI/Appearance/SystemThemeManager.cs b/source/RevitLookup.UI/Appearance/SystemThemeManager.cs similarity index 100% rename from RevitLookup.UI/Appearance/SystemThemeManager.cs rename to source/RevitLookup.UI/Appearance/SystemThemeManager.cs diff --git a/RevitLookup.UI/Appearance/SystemThemeWatcher.cs b/source/RevitLookup.UI/Appearance/SystemThemeWatcher.cs similarity index 100% rename from RevitLookup.UI/Appearance/SystemThemeWatcher.cs rename to source/RevitLookup.UI/Appearance/SystemThemeWatcher.cs diff --git a/RevitLookup.UI/Appearance/ThemeChangedEvent.cs b/source/RevitLookup.UI/Appearance/ThemeChangedEvent.cs similarity index 100% rename from RevitLookup.UI/Appearance/ThemeChangedEvent.cs rename to source/RevitLookup.UI/Appearance/ThemeChangedEvent.cs diff --git a/RevitLookup.UI/Appearance/WindowBackgroundManager.cs b/source/RevitLookup.UI/Appearance/WindowBackgroundManager.cs similarity index 93% rename from RevitLookup.UI/Appearance/WindowBackgroundManager.cs rename to source/RevitLookup.UI/Appearance/WindowBackgroundManager.cs index fba0fa750..d285939e5 100644 --- a/RevitLookup.UI/Appearance/WindowBackgroundManager.cs +++ b/source/RevitLookup.UI/Appearance/WindowBackgroundManager.cs @@ -5,6 +5,7 @@ using Wpf.Ui.Controls; using Wpf.Ui.Interop; +using Wpf.Ui.Win32; namespace Wpf.Ui.Appearance; @@ -70,11 +71,15 @@ bool forceBackground { backdrop = WindowBackdropType.None; } + + // This was required to update the background when moving from a HC theme to light/dark theme. However, this breaks theme proper light/dark theme changing on Windows 10. else { - _ = WindowBackdrop.RemoveBackground(window); + if (Utilities.IsOSWindows11OrNewer) + { + _ = WindowBackdrop.RemoveBackground(window); + } } - _ = WindowBackdrop.ApplyBackdrop(window, backdrop); if (applicationTheme is ApplicationTheme.Dark) { diff --git a/RevitLookup.UI/Application.cs b/source/RevitLookup.UI/Application.cs similarity index 100% rename from RevitLookup.UI/Application.cs rename to source/RevitLookup.UI/Application.cs diff --git a/RevitLookup.UI/AutomationPeers/CardControlAutomationPeer.cs b/source/RevitLookup.UI/AutomationPeers/CardControlAutomationPeer.cs similarity index 100% rename from RevitLookup.UI/AutomationPeers/CardControlAutomationPeer.cs rename to source/RevitLookup.UI/AutomationPeers/CardControlAutomationPeer.cs diff --git a/RevitLookup.UI/ContentDialogService.cs b/source/RevitLookup.UI/ContentDialogService.cs similarity index 100% rename from RevitLookup.UI/ContentDialogService.cs rename to source/RevitLookup.UI/ContentDialogService.cs diff --git a/RevitLookup.UI/Controls/Anchor/Anchor.bmp b/source/RevitLookup.UI/Controls/Anchor/Anchor.bmp similarity index 100% rename from RevitLookup.UI/Controls/Anchor/Anchor.bmp rename to source/RevitLookup.UI/Controls/Anchor/Anchor.bmp diff --git a/RevitLookup.UI/Controls/Anchor/Anchor.cs b/source/RevitLookup.UI/Controls/Anchor/Anchor.cs similarity index 100% rename from RevitLookup.UI/Controls/Anchor/Anchor.cs rename to source/RevitLookup.UI/Controls/Anchor/Anchor.cs diff --git a/RevitLookup.UI/Controls/Anchor/Anchor.xaml b/source/RevitLookup.UI/Controls/Anchor/Anchor.xaml similarity index 100% rename from RevitLookup.UI/Controls/Anchor/Anchor.xaml rename to source/RevitLookup.UI/Controls/Anchor/Anchor.xaml diff --git a/RevitLookup.UI/Controls/Arc/Arc.bmp b/source/RevitLookup.UI/Controls/Arc/Arc.bmp similarity index 100% rename from RevitLookup.UI/Controls/Arc/Arc.bmp rename to source/RevitLookup.UI/Controls/Arc/Arc.bmp diff --git a/RevitLookup.UI/Controls/Arc/Arc.cs b/source/RevitLookup.UI/Controls/Arc/Arc.cs similarity index 100% rename from RevitLookup.UI/Controls/Arc/Arc.cs rename to source/RevitLookup.UI/Controls/Arc/Arc.cs diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.bmp b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.bmp similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.bmp rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.bmp diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.cs b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.cs similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.cs rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.cs diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.xaml b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.xaml similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.xaml rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBox.xaml diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxQuerySubmittedEventArgs.cs b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxQuerySubmittedEventArgs.cs similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxQuerySubmittedEventArgs.cs rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxQuerySubmittedEventArgs.cs diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxSuggestionChosenEventArgs.cs b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxSuggestionChosenEventArgs.cs similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxSuggestionChosenEventArgs.cs rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxSuggestionChosenEventArgs.cs diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxTextChangedEventArgs.cs b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxTextChangedEventArgs.cs similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxTextChangedEventArgs.cs rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestBoxTextChangedEventArgs.cs diff --git a/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestionBoxTextChangeReason.cs b/source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestionBoxTextChangeReason.cs similarity index 100% rename from RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestionBoxTextChangeReason.cs rename to source/RevitLookup.UI/Controls/AutoSuggestBox/AutoSuggestionBoxTextChangeReason.cs diff --git a/RevitLookup.UI/Controls/Badge/Badge.cs b/source/RevitLookup.UI/Controls/Badge/Badge.cs similarity index 100% rename from RevitLookup.UI/Controls/Badge/Badge.cs rename to source/RevitLookup.UI/Controls/Badge/Badge.cs diff --git a/RevitLookup.UI/Controls/Badge/Badge.xaml b/source/RevitLookup.UI/Controls/Badge/Badge.xaml similarity index 86% rename from RevitLookup.UI/Controls/Badge/Badge.xaml rename to source/RevitLookup.UI/Controls/Badge/Badge.xaml index 4d2d1544c..54118fc91 100644 --- a/RevitLookup.UI/Controls/Badge/Badge.xaml +++ b/source/RevitLookup.UI/Controls/Badge/Badge.xaml @@ -11,16 +11,8 @@ xmlns:controls="clr-namespace:Wpf.Ui.Controls"> + + + + + + + + \ No newline at end of file diff --git a/RevitLookup/Views/Resources/Menus.xaml.cs b/source/RevitLookup/Views/Controls/DataGrid/DataGridGroupStyle.xaml.cs similarity index 86% rename from RevitLookup/Views/Resources/Menus.xaml.cs rename to source/RevitLookup/Views/Controls/DataGrid/DataGridGroupStyle.xaml.cs index 48e64f44e..8aef8d09d 100644 --- a/RevitLookup/Views/Resources/Menus.xaml.cs +++ b/source/RevitLookup/Views/Controls/DataGrid/DataGridGroupStyle.xaml.cs @@ -18,11 +18,12 @@ // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. -namespace RevitLookup.Views.Resources; +// ReSharper disable once CheckNamespace +namespace RevitLookup.Views.Controls; -sealed partial class Menus +public partial class DataGridGroupStyle { - public Menus() + public DataGridGroupStyle() { InitializeComponent(); } diff --git a/source/RevitLookup/Views/Controls/DataGrid/DataGridRowStyle.xaml b/source/RevitLookup/Views/Controls/DataGrid/DataGridRowStyle.xaml new file mode 100644 index 000000000..73a3475ab --- /dev/null +++ b/source/RevitLookup/Views/Controls/DataGrid/DataGridRowStyle.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/RevitLookup/Views/Controls/TreeView/TreeViewItemTemplate.xaml b/source/RevitLookup/Views/Controls/TreeView/TreeViewItemTemplate.xaml new file mode 100644 index 000000000..db83f9f88 --- /dev/null +++ b/source/RevitLookup/Views/Controls/TreeView/TreeViewItemTemplate.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/RevitLookup/Views/Converters/DescriptorLabelConverters.cs b/source/RevitLookup/Views/Converters/DescriptorLabelConverters.cs new file mode 100644 index 000000000..586424820 --- /dev/null +++ b/source/RevitLookup/Views/Converters/DescriptorLabelConverters.cs @@ -0,0 +1,79 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Globalization; +using System.Windows.Data; +using System.Windows.Markup; +using RevitLookup.Core.Objects; + +namespace RevitLookup.Views.Converters; + +public abstract class DescriptorConverter : MarkupExtension, IValueConverter +{ + protected string ConvertInvalidNames(string text) + { + return text switch + { + null => "", + "" => "", + _ => text + }; + } + + protected static string CreateCombinedName(Descriptor descriptor) + { + if (string.IsNullOrEmpty(descriptor.Name)) return descriptor.Name; + return string.IsNullOrEmpty(descriptor.Description) ? descriptor.Name : $"{descriptor.Description}: {descriptor.Name}"; + } + + protected static string CreateSingleName(Descriptor descriptor) + { + if (string.IsNullOrEmpty(descriptor.Name)) return descriptor.Name; + return string.IsNullOrEmpty(descriptor.Description) ? descriptor.Name : descriptor.Description; + } + + public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } +} + +public sealed class SingleDescriptorConverter : DescriptorConverter +{ + public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return ConvertInvalidNames(CreateSingleName((Descriptor) value!)); + } +} + +public sealed class CombinedDescriptorConverter : DescriptorConverter +{ + public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return ConvertInvalidNames(CreateCombinedName((Descriptor) value!)); + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Converters/DescriptorConverters.cs b/source/RevitLookup/Views/Converters/IconDescriptorConverter.cs similarity index 55% rename from RevitLookup/Views/Converters/DescriptorConverters.cs rename to source/RevitLookup/Views/Converters/IconDescriptorConverter.cs index 7a98cfbbd..3683484ac 100644 --- a/RevitLookup/Views/Converters/DescriptorConverters.cs +++ b/source/RevitLookup/Views/Converters/IconDescriptorConverter.cs @@ -22,106 +22,11 @@ using System.Globalization; using System.Windows.Data; using System.Windows.Markup; -using RevitLookup.Core.ComponentModel.Descriptors; -using RevitLookup.Core.Contracts; using RevitLookup.Core.Enums; -using RevitLookup.Core.Objects; using Wpf.Ui.Controls; namespace RevitLookup.Views.Converters; -public abstract class DescriptorConverter : MarkupExtension, IValueConverter -{ - public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - - protected static string CreateCombinedName(Descriptor descriptor) - { - if (string.IsNullOrEmpty(descriptor.Name)) return descriptor.Name; - return string.IsNullOrEmpty(descriptor.Description) ? descriptor.Name : $"{descriptor.Description}: {descriptor.Name}"; - } - - protected static string CreateSingleName(Descriptor descriptor) - { - if (string.IsNullOrEmpty(descriptor.Name)) return descriptor.Name; - return string.IsNullOrEmpty(descriptor.Description) ? descriptor.Name : descriptor.Description; - } - - protected string ConvertInvalidNames(string text) - { - return text switch - { - null => "", - "" => "", - _ => text - }; - } - - public override object ProvideValue(IServiceProvider serviceProvider) - { - return this; - } -} - -public sealed class SingleDescriptorConverter : DescriptorConverter -{ - public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return ConvertInvalidNames(CreateSingleName((Descriptor) value!)); - } -} - -public sealed class CombinedDescriptorConverter : DescriptorConverter -{ - public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return ConvertInvalidNames(CreateCombinedName((Descriptor) value!)); - } -} - -public sealed class HandledDescriptorConverter : MarkupExtension, IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var descriptor = (Descriptor) value!; - if (descriptor is IDescriptorEnumerator enumerator) return !enumerator.IsEmpty; - return descriptor is IDescriptorCollector; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - - public override object ProvideValue(IServiceProvider serviceProvider) - { - return this; - } -} - -public sealed class ExceptionDescriptorConverter : MarkupExtension, IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var descriptor = (Descriptor) value!; - return descriptor is ExceptionDescriptor; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - - public override object ProvideValue(IServiceProvider serviceProvider) - { - return this; - } -} - public sealed class IconDescriptorConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/source/RevitLookup/Views/Converters/ObjectColorConverter.cs b/source/RevitLookup/Views/Converters/ObjectColorConverter.cs new file mode 100644 index 000000000..d20a9d786 --- /dev/null +++ b/source/RevitLookup/Views/Converters/ObjectColorConverter.cs @@ -0,0 +1,50 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Globalization; +using System.Windows.Data; +using System.Windows.Markup; +using Autodesk.Revit.DB; + +namespace RevitLookup.Views.Converters; + +public class ObjectColorConverter : MarkupExtension, IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value switch + { + Color {IsValid: false} => System.Windows.Media.Colors.Transparent, + Color color => System.Windows.Media.Color.FromArgb(byte.MaxValue, color.Red, color.Green, color.Blue), + System.Windows.Media.Color color => color, + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Converters/TreeViewSourceConverter.cs b/source/RevitLookup/Views/Converters/TreeViewSourceConverter.cs similarity index 100% rename from RevitLookup/Views/Converters/TreeViewSourceConverter.cs rename to source/RevitLookup/Views/Converters/TreeViewSourceConverter.cs diff --git a/RevitLookup/Views/Converters/ValueConverters.cs b/source/RevitLookup/Views/Converters/ValueConverters.cs similarity index 100% rename from RevitLookup/Views/Converters/ValueConverters.cs rename to source/RevitLookup/Views/Converters/ValueConverters.cs diff --git a/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml b/source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml similarity index 100% rename from RevitLookup/Views/Dialogs/OpenSourceDialog.xaml rename to source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml diff --git a/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml.cs b/source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml.cs similarity index 100% rename from RevitLookup/Views/Dialogs/OpenSourceDialog.xaml.cs rename to source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml.cs diff --git a/RevitLookup/Views/Dialogs/SearchElementsDialog.xaml b/source/RevitLookup/Views/Dialogs/SearchElementsDialog.xaml similarity index 100% rename from RevitLookup/Views/Dialogs/SearchElementsDialog.xaml rename to source/RevitLookup/Views/Dialogs/SearchElementsDialog.xaml diff --git a/RevitLookup/Views/Dialogs/SearchElementsDialog.xaml.cs b/source/RevitLookup/Views/Dialogs/SearchElementsDialog.xaml.cs similarity index 100% rename from RevitLookup/Views/Dialogs/SearchElementsDialog.xaml.cs rename to source/RevitLookup/Views/Dialogs/SearchElementsDialog.xaml.cs diff --git a/RevitLookup/Views/Dialogs/UnitsDialog.xaml b/source/RevitLookup/Views/Dialogs/UnitsDialog.xaml similarity index 100% rename from RevitLookup/Views/Dialogs/UnitsDialog.xaml rename to source/RevitLookup/Views/Dialogs/UnitsDialog.xaml diff --git a/RevitLookup/Views/Dialogs/UnitsDialog.xaml.cs b/source/RevitLookup/Views/Dialogs/UnitsDialog.xaml.cs similarity index 100% rename from RevitLookup/Views/Dialogs/UnitsDialog.xaml.cs rename to source/RevitLookup/Views/Dialogs/UnitsDialog.xaml.cs diff --git a/RevitLookup/Views/Extensions/ContextMenuExtensions.cs b/source/RevitLookup/Views/Extensions/ContextMenuExtensions.cs similarity index 100% rename from RevitLookup/Views/Extensions/ContextMenuExtensions.cs rename to source/RevitLookup/Views/Extensions/ContextMenuExtensions.cs diff --git a/source/RevitLookup/Views/Markup/MenusDictionary.cs b/source/RevitLookup/Views/Markup/MenusDictionary.cs new file mode 100644 index 000000000..9d67a0052 --- /dev/null +++ b/source/RevitLookup/Views/Markup/MenusDictionary.cs @@ -0,0 +1,41 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Markup; + +namespace RevitLookup.Views.Markup; + +[Localizability(LocalizationCategory.Ignore)] +[Ambient] +[UsableDuringInitialization(true)] +public class MenusDictionary : ResourceDictionary +{ + private const string DictionaryUri = "pack://application:,,,/RevitLookup;component/Views/Resources/Menus.xaml"; + + /// + /// Initializes a new instance of the class. + /// Default constructor defining of the RevitLookup UI controls dictionary. + /// + public MenusDictionary() + { + Source = new Uri(DictionaryUri, UriKind.Absolute); + } +} diff --git a/source/RevitLookup/Views/Markup/StylesDictionary.cs b/source/RevitLookup/Views/Markup/StylesDictionary.cs new file mode 100644 index 000000000..33effa8d1 --- /dev/null +++ b/source/RevitLookup/Views/Markup/StylesDictionary.cs @@ -0,0 +1,41 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Markup; + +namespace RevitLookup.Views.Markup; + +[Localizability(LocalizationCategory.Ignore)] +[Ambient] +[UsableDuringInitialization(true)] +public class StylesDictionary : ResourceDictionary +{ + private const string DictionaryUri = "pack://application:,,,/RevitLookup;component/Views/Resources/RevitLookup.Ui.xaml"; + + /// + /// Initializes a new instance of the class. + /// Default constructor defining of the RevitLookup UI controls dictionary. + /// + public StylesDictionary() + { + Source = new Uri(DictionaryUri, UriKind.Absolute); + } +} diff --git a/RevitLookup/Views/Markup/ThemesDictionary.cs b/source/RevitLookup/Views/Markup/ThemesDictionary.cs similarity index 100% rename from RevitLookup/Views/Markup/ThemesDictionary.cs rename to source/RevitLookup/Views/Markup/ThemesDictionary.cs diff --git a/RevitLookup/Views/Pages/AboutView.xaml b/source/RevitLookup/Views/Pages/AboutView.xaml similarity index 91% rename from RevitLookup/Views/Pages/AboutView.xaml rename to source/RevitLookup/Views/Pages/AboutView.xaml index 23cbebc2d..1d1ba1be8 100644 --- a/RevitLookup/Views/Pages/AboutView.xaml +++ b/source/RevitLookup/Views/Pages/AboutView.xaml @@ -9,7 +9,8 @@ xmlns:pages="clr-namespace:RevitLookup.Views.Pages" mc:Ignorable="d" d:DataContext="{d:DesignInstance pages:AboutView}"> - + + + + + + + + @@ -212,5 +230,6 @@ NavigateUri="https://github.com/jeremytammik/RevitLookup/issues/new" /> - + + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/AboutView.xaml.cs b/source/RevitLookup/Views/Pages/AboutView.xaml.cs similarity index 100% rename from RevitLookup/Views/Pages/AboutView.xaml.cs rename to source/RevitLookup/Views/Pages/AboutView.xaml.cs diff --git a/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs new file mode 100644 index 000000000..d6f04a538 --- /dev/null +++ b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ContextMenu.cs @@ -0,0 +1,162 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; +using RevitLookup.Views.Extensions; +using RevitLookup.Views.Utils; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + /// + /// Tree view context menu + /// + private void CreateTreeContextMenu(Descriptor descriptor, FrameworkElement row) + { + var contextMenu = new ContextMenu + { + Resources = Resources + }; + + contextMenu.AddMenuItem("CopyMenuItem") + .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Name)) + .SetShortcut(row, ModifierKeys.Control, Key.C); + contextMenu.AddMenuItem("HelpMenuItem") + .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName)) + .SetShortcut(row, Key.F1); + + if (descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); + row.ContextMenu = contextMenu; + } + + /// + /// Data grid context menu + /// + private void CreateGridContextMenu(DataGrid dataGrid) + { + var contextMenu = new ContextMenu + { + Resources = Resources + }; + + contextMenu.AddMenuItem("RefreshMenuItem") + .SetCommand(ViewModel.RefreshMembersCommand) + .SetGestureText(Key.F5); + + contextMenu.AddSeparator(); + contextMenu.AddLabel("Columns"); + + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Time") + .SetCommand(dataGrid.Columns[2].Visibility == Visibility.Visible, parameter => + { + dataGrid.Columns[2].Visibility = parameter ? Visibility.Collapsed : Visibility.Visible; + _settingsService.ShowTimeColumn = !parameter; + }); + + contextMenu.AddSeparator(); + contextMenu.AddLabel("Show"); + + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Events") + .SetCommand(_settingsService.IncludeEvents, parameter => + { + _settingsService.IncludeEvents = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Extensions") + .SetCommand(_settingsService.IncludeExtensions, parameter => + { + _settingsService.IncludeExtensions = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Fields") + .SetCommand(_settingsService.IncludeFields, parameter => + { + _settingsService.IncludeFields = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Non-public") + .SetCommand(_settingsService.IncludePrivate, parameter => + { + _settingsService.IncludePrivate = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Root hierarchy") + .SetCommand(_settingsService.IncludeRootHierarchy, parameter => + { + _settingsService.IncludeRootHierarchy = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Static") + .SetCommand(_settingsService.IncludeStatic, parameter => + { + _settingsService.IncludeStatic = !parameter; + return RefreshGridAsync(); + }); + contextMenu.AddMenuItem("CheckableMenuItem") + .SetHeader("Unsupported") + .SetCommand(_settingsService.IncludeUnsupported, parameter => + { + _settingsService.IncludeUnsupported = !parameter; + return RefreshGridAsync(); + }); + + dataGrid.ContextMenu = contextMenu; + } + + /// + /// Data grid row context menu + /// + private void CreateGridRowContextMenu(Descriptor descriptor, FrameworkElement row) + { + var contextMenu = new ContextMenu + { + Resources = Resources + }; + + contextMenu.AddMenuItem("CopyMenuItem") + .SetCommand(descriptor, parameter => Clipboard.SetText($"{parameter.Name}: {parameter.Value.Descriptor.Name}")) + .SetShortcut(row, ModifierKeys.Control, Key.C); + + contextMenu.AddMenuItem("CopyMenuItem") + .SetHeader("Copy value") + .SetCommand(descriptor, parameter => Clipboard.SetText(parameter.Value.Descriptor.Name)) + .SetShortcut(row, ModifierKeys.Control | ModifierKeys.Shift, Key.C) + .SetAvailability(descriptor.Value.Descriptor.Name is not null); + + contextMenu.AddMenuItem("HelpMenuItem") + .SetCommand(descriptor, parameter => HelpUtils.ShowHelp(parameter.TypeFullName, parameter.Name)) + .SetShortcut(row, new KeyGesture(Key.F1)); + + if (descriptor.Value.Descriptor is IDescriptorConnector connector) connector.RegisterMenu(contextMenu, row); + row.ContextMenu = contextMenu; + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs new file mode 100644 index 000000000..5a9db2f5e --- /dev/null +++ b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Gestures.cs @@ -0,0 +1,57 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows.Input; +using CommunityToolkit.Mvvm.Input; +using Wpf.Ui.Controls; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase : INavigationAware +{ + /// + /// Page shortcuts + /// + private void AddShortcuts() + { + var command = new AsyncRelayCommand(() => ViewModel.RefreshMembersCommand.ExecuteAsync(null)); + InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.F5))); + } + + /// + /// Window shortcuts + /// + private void OnPageKeyPressed(object sender, KeyEventArgs e) + { + if (SearchBoxControl.IsKeyboardFocused) return; + if (e.KeyboardDevice.Modifiers != ModifierKeys.None) return; + if (e.Key is >= Key.D0 and <= Key.Z or >= Key.NumPad0 and <= Key.NumPad9) SearchBoxControl.Focus(); + } + + public void OnNavigatedTo() + { + Wpf.Ui.Application.MainWindow.PreviewKeyDown += OnPageKeyPressed; + } + + public void OnNavigatedFrom() + { + Wpf.Ui.Application.MainWindow.PreviewKeyDown -= OnPageKeyPressed; + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs new file mode 100644 index 000000000..83476e3a5 --- /dev/null +++ b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Navigation.cs @@ -0,0 +1,134 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; +using RevitLookup.Views.Utils; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + /// + /// Handle tree view select event + /// + /// + /// Collect data for selected item + /// + protected void OnTreeItemSelected(object sender, RoutedPropertyChangedEventArgs args) + { + switch (args.NewValue) + { + case SnoopableObject snoopableObject: + ViewModel.SelectedObject = snoopableObject; + break; + case CollectionViewGroup: + ViewModel.SelectedObject = null; + break; + default: + //Internal __Canon object + return; + } + + ViewModel.FetchMembersCommand.Execute(null); + } + + /// + /// Handle tree view click event + /// + /// + /// Open new window for navigation on Ctrl pressed + /// + private void OnTreeItemClicked(object sender, RoutedEventArgs args) + { + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) return; + args.Handled = true; + + var element = (FrameworkElement) args.OriginalSource; + switch (element.DataContext) + { + case SnoopableObject item: + ViewModel.Navigate(item); + break; + case CollectionViewGroup group: + ViewModel.Navigate(group.Items.Cast().ToArray()); + break; + } + } + + /// + /// Handle data grid click event + /// + /// + /// Open new window for navigation + /// + private void OnGridRowClicked(object sender, RoutedEventArgs args) + { + var row = (DataGridRow) sender; + var context = (Descriptor) row.DataContext; + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) + { + if (context.Value.Descriptor is not IDescriptorCollector) return; + if (context.Value.Descriptor is IDescriptorEnumerator {IsEmpty: true}) return; + } + + ViewModel.Navigate(context.Value); + } + + /// + /// Handle cursor interaction + /// + protected void OnPresenterCursorInteracted(object sender, MouseEventArgs args) + { + var presenter = (FrameworkElement) sender; + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) + { + presenter.Cursor = null; + return; + } + + FrameworkElement item = sender switch + { + DataGrid => VisualUtils.FindVisualParent((DependencyObject) args.OriginalSource), + TreeView => VisualUtils.FindVisualParent((DependencyObject) args.OriginalSource), + _ => throw new NotSupportedException() + }; + + if (item is null) + { + presenter.Cursor = null; + return; + } + + presenter.Cursor = Cursors.Hand; + presenter.PreviewKeyUp += OnPresenterCursorRestored; + } + + private void OnPresenterCursorRestored(object sender, KeyEventArgs e) + { + var presenter = (FrameworkElement) sender; + presenter.PreviewKeyUp -= OnPresenterCursorRestored; + presenter.Cursor = null; + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Styles.cs b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Styles.cs new file mode 100644 index 000000000..0f7c9542e --- /dev/null +++ b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.Styles.cs @@ -0,0 +1,98 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using RevitLookup.Core.ComponentModel.Descriptors; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + /// + /// Data grid row style selector + /// + private void SelectDataGridRowStyle(DataGridRow row) + { + var rowDescriptor = (Descriptor) row.DataContext; + var styleName = rowDescriptor.Value.Descriptor switch + { + ExceptionDescriptor => "ExceptionDataGridRowStyle", + IDescriptorEnumerator {IsEmpty: false} => "HandleDataGridRowStyle", + IDescriptorEnumerator => "DefaultLookupDataGridRowStyle", + IDescriptorCollector => "HandleDataGridRowStyle", + _ => "DefaultLookupDataGridRowStyle" + }; + + row.Style = (Style) FindResource(styleName); + } +} + +public class DataGridCellStyleSelector : DataTemplateSelector +{ + /// + /// Data grid cell style selector + /// + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (item is null) return null; + + var descriptor = (Descriptor) item; + var presenter = (FrameworkElement) container; + var templateName = descriptor.Value.Descriptor switch + { + ColorDescriptor => "DataGridColorCellTemplate", + ColorMediaDescriptor => "DataGridColorCellTemplate", + _ => "DefaultLookupDataGridCellTemplate" + }; + + return (DataTemplate) presenter.FindResource(templateName); + } +} + +public class TreeViewItemTemplateSelector : DataTemplateSelector +{ + /// + /// Tree view row style selector + /// + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (item is null) return null; + + var presenter = (FrameworkElement) container; + var templateName = item switch + { + CollectionViewGroup => "DefaultLookupTreeViewGroupTemplate", + SnoopableObject snoopableObject => snoopableObject.Descriptor switch + { + ColorDescriptor => "TreeViewColorItemTemplate", + ColorMediaDescriptor => "TreeViewColorItemTemplate", + _ => "DefaultLookupTreeViewItemTemplate" + }, + + _ => "DefaultLookupTreeViewItemTemplate" + }; + + return (DataTemplate) presenter.FindResource(templateName); + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs new file mode 100644 index 000000000..ce9d235a5 --- /dev/null +++ b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.ToolTips.cs @@ -0,0 +1,84 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Text; +using System.Windows; +using System.Windows.Controls; +using RevitLookup.Core.Enums; +using RevitLookup.Core.Objects; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase +{ + /// + /// Create tree view tooltips + /// + private void CreateTreeTooltip(Descriptor descriptor, FrameworkElement row) + { + row.ToolTip = new ToolTip + { + Content = new StringBuilder() + .Append("Type: ") + .AppendLine(descriptor.Type) + .Append("Value: ") + .Append(descriptor.Name) + .ToString() + }; + } + + /// + /// Create data grid tooltips + /// + private void CreateGridRowTooltip(Descriptor descriptor, FrameworkElement row) + { + var builder = new StringBuilder(); + + if ((descriptor.MemberAttributes & MemberAttributes.Private) != 0) builder.Append("Private "); + if ((descriptor.MemberAttributes & MemberAttributes.Static) != 0) builder.Append("Static "); + if ((descriptor.MemberAttributes & MemberAttributes.Property) != 0) builder.Append("Property: "); + if ((descriptor.MemberAttributes & MemberAttributes.Extension) != 0) builder.Append("Extension: "); + if ((descriptor.MemberAttributes & MemberAttributes.Method) != 0) builder.Append("Method: "); + if ((descriptor.MemberAttributes & MemberAttributes.Event) != 0) builder.Append("Event: "); + if ((descriptor.MemberAttributes & MemberAttributes.Field) != 0) builder.Append("Field: "); + + builder.AppendLine(descriptor.Name) + .Append("Type: ") + .AppendLine(descriptor.Value.Descriptor.Type) + .Append("Value: ") + .Append(descriptor.Value.Descriptor.Name); + + if (descriptor.Value.Descriptor.Description is not null) + builder.AppendLine() + .Append("Description: ") + .Append(descriptor.Value.Descriptor.Description); + + if (descriptor.ComputationTime > 0) + builder.AppendLine() + .Append("Time: ") + .Append(descriptor.ComputationTime) + .Append(" ms"); + + row.ToolTip = new ToolTip + { + Content = builder.ToString() + }; + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs new file mode 100644 index 000000000..55a040317 --- /dev/null +++ b/source/RevitLookup/Views/Pages/Abstraction/SnoopViewBase.xaml.cs @@ -0,0 +1,283 @@ +// Copyright 2003-2023 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Collections; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using RevitLookup.Core.Objects; +using RevitLookup.Services.Contracts; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Utils; +using Wpf.Ui.Controls; +using DataGrid = Wpf.Ui.Controls.DataGrid; +using TreeView = Wpf.Ui.Controls.TreeView; + +namespace RevitLookup.Views.Pages.Abstraction; + +public partial class SnoopViewBase : Page, INavigableView +{ + private readonly TreeView _treeViewControl; + private readonly DataGrid _dataGridControl; + private readonly ISettingsService _settingsService; + + protected SnoopViewBase(ISettingsService settingsService) + { + _settingsService = settingsService; + AddShortcuts(); + } + + protected TreeView TreeViewControl + { + get => _treeViewControl; + init + { + _treeViewControl = value; + OnTreeChanged(value); + } + } + + protected DataGrid DataGridControl + { + get => _dataGridControl; + init + { + _dataGridControl = value; + OnGridChanged(value); + } + } + + protected UIElement SearchBoxControl { get; init; } + public ISnoopViewModel ViewModel { get; protected init; } + + /// + /// Handle data grid reference changed event + /// + /// + /// Data grid initialization + /// + private void OnTreeChanged(TreeView control) + { + control.ItemContainerGenerator.StatusChanged += OnTreeViewItemGenerated; + } + + /// + /// Handle tree view source changed + /// + /// + /// Tree view initialization, apply transition + /// + protected void OnTreeSourceChanged(object sender, IEnumerable enumerable) + { + if (IsLoaded) + { + SetupTreeView(); + return; + } + + Loaded += OnLoaded; + return; + + void OnLoaded(object o, RoutedEventArgs args) + { + Loaded -= OnLoaded; + SetupTreeView(); + } + } + + /// + /// Handle tree view item loaded + /// + /// + /// TreeView item customization after loading + /// + private void OnTreeViewItemGenerated(object sender, EventArgs _) + { + var generator = (ItemContainerGenerator) sender; + if (generator.Status == GeneratorStatus.ContainersGenerated) + { + foreach (var item in generator.Items) + { + var treeItem = (ItemsControl) generator.ContainerFromItem(item); + if (treeItem is null) continue; + + treeItem.Loaded -= OnTreeItemLoaded; + treeItem.PreviewMouseLeftButtonUp -= OnTreeItemClicked; + + treeItem.Loaded += OnTreeItemLoaded; + treeItem.PreviewMouseLeftButtonUp += OnTreeItemClicked; + + if (treeItem.Items.Count > 0) + { + treeItem.ItemContainerGenerator.StatusChanged -= OnTreeViewItemGenerated; + treeItem.ItemContainerGenerator.StatusChanged += OnTreeViewItemGenerated; + } + } + } + } + + /// + /// Handle tree view loaded event + /// + /// + /// Create tree view tooltips, menus + /// + private void OnTreeItemLoaded(object sender, RoutedEventArgs args) + { + var element = (FrameworkElement) sender; + switch (element.DataContext) + { + case SnoopableObject context: + CreateTreeTooltip(context.Descriptor, element); + CreateTreeContextMenu(context.Descriptor, element); + break; + } + } + + /// + /// Handle tree view reference changed event + /// + /// + /// Tree view initialization, apply transition + /// + private async void SetupTreeView() + { + // Await Frame transition. GetMembers freezes the thread and breaks the animation + await Task.Delay(_settingsService.TransitionDuration); + + // if (TryRestoreSelection()) return; + + ExpandFirstTreeGroup(); + } + + /// + /// Handle data grid reference changed event + /// + /// + /// Data grid initialization, validation + /// + private void OnGridChanged(DataGrid control) + { + control.Loaded += (sender, _) => + { + var dataGrid = (DataGrid) sender; + ValidateTimeColumn(dataGrid); + CreateGridContextMenu(dataGrid); + }; + control.ItemsSourceChanged += OnGridItemsSourceChanged; + } + + /// + /// Handle data grid source changed + /// + /// + /// Group and sort items + /// + private void OnGridItemsSourceChanged(object sender, EventArgs _) + { + var dataGrid = (DataGrid) sender; + + //Clear shapingStorage for remove duplications. WpfBug? + dataGrid.Items.GroupDescriptions!.Clear(); + dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Depth), ListSortDirection.Descending)); + dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.MemberAttributes), ListSortDirection.Ascending)); + dataGrid.Items.SortDescriptions.Add(new SortDescription(nameof(Descriptor.Name), ListSortDirection.Ascending)); + dataGrid.Items.GroupDescriptions.Add(new PropertyGroupDescription(nameof(Descriptor.Type))); + } + + /// + /// Handle data grid row loading event + /// + /// + /// Select row style + /// + protected void OnGridRowLoading(object sender, DataGridRowEventArgs args) + { + var row = args.Row; + row.Loaded += OnGridRowLoaded; + row.PreviewMouseLeftButtonUp += OnGridRowClicked; + SelectDataGridRowStyle(row); + } + + /// + /// Handle data grid row loaded event + /// + /// + /// Create tooltips, context menu + /// + protected void OnGridRowLoaded(object sender, RoutedEventArgs args) + { + var element = (FrameworkElement) sender; + var context = (Descriptor) element.DataContext; + CreateGridRowTooltip(context, element); + CreateGridRowContextMenu(context, element); + } + + /// + /// Restore the previous selection when cancelling a search + /// + [Obsolete("Low performance")] + private bool TryRestoreSelection() + { + if (ViewModel.SelectedObject is null) return false; + + var treeViewItem = VisualUtils.GetTreeViewItem(TreeViewControl, ViewModel.SelectedObject); + if (treeViewItem is null || treeViewItem.IsSelected) return false; + + TreeViewControl.SelectedItemChanged -= OnTreeItemSelected; + treeViewItem.IsSelected = true; + TreeViewControl.SelectedItemChanged += OnTreeItemSelected; + return true; + } + + /// + /// Expand first tree view group after navigation + /// + private void ExpandFirstTreeGroup() + { + if (TreeViewControl.Items.Count > 3) return; + + var rootItem = VisualUtils.GetTreeViewItem(TreeViewControl, 0); + if (rootItem is null) return; + + var nestedItem = VisualUtils.GetTreeViewItem(rootItem, 0); + if (nestedItem is null) return; + + nestedItem.IsSelected = true; + } + + /// + /// Show/hide time column + /// + private void ValidateTimeColumn(System.Windows.Controls.DataGrid control) + { + control.Columns[2].Visibility = _settingsService.ShowTimeColumn ? Visibility.Visible : Visibility.Collapsed; + } + + /// + /// Recollect all data on the selected item + /// + private Task RefreshGridAsync() + { + return ViewModel.RefreshMembersCommand.ExecuteAsync(null); + } +} \ No newline at end of file diff --git a/RevitLookup/Views/Pages/DashboardView.xaml b/source/RevitLookup/Views/Pages/DashboardView.xaml similarity index 99% rename from RevitLookup/Views/Pages/DashboardView.xaml rename to source/RevitLookup/Views/Pages/DashboardView.xaml index a68072551..eb57854ef 100644 --- a/RevitLookup/Views/Pages/DashboardView.xaml +++ b/source/RevitLookup/Views/Pages/DashboardView.xaml @@ -8,7 +8,8 @@ xmlns:pages="clr-namespace:RevitLookup.Views.Pages" mc:Ignorable="d" d:DataContext="{d:DesignInstance pages:DashboardView}"> - + + - + + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/DashboardView.xaml.cs b/source/RevitLookup/Views/Pages/DashboardView.xaml.cs similarity index 100% rename from RevitLookup/Views/Pages/DashboardView.xaml.cs rename to source/RevitLookup/Views/Pages/DashboardView.xaml.cs diff --git a/source/RevitLookup/Views/Pages/EventsView.xaml b/source/RevitLookup/Views/Pages/EventsView.xaml new file mode 100644 index 000000000..144af4248 --- /dev/null +++ b/source/RevitLookup/Views/Pages/EventsView.xaml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/EventsView.xaml.cs b/source/RevitLookup/Views/Pages/EventsView.xaml.cs similarity index 80% rename from RevitLookup/Views/Pages/EventsView.xaml.cs rename to source/RevitLookup/Views/Pages/EventsView.xaml.cs index 7cccebec5..280c18e6a 100644 --- a/RevitLookup/Views/Pages/EventsView.xaml.cs +++ b/source/RevitLookup/Views/Pages/EventsView.xaml.cs @@ -19,23 +19,22 @@ // (Rights in Technical Data and Computer Software), as applicable. using RevitLookup.Services.Contracts; -using RevitLookup.ViewModels.Contracts; using RevitLookup.ViewModels.Pages; namespace RevitLookup.Views.Pages; public sealed partial class EventsView { - public EventsView(IServiceProvider serviceProvider, ISettingsService settingsService) : base(settingsService) + public EventsView(EventsViewModel viewModel, ISettingsService settingsService) : base(settingsService) { InitializeComponent(); + ViewModel = viewModel; DataGridControl = DataGrid; TreeViewControl = TreeView; SearchBoxControl = SearchBox; - TreeView.SelectedItemChanged += OnTreeSelectionChanged; + TreeView.SelectedItemChanged += OnTreeItemSelected; - ViewModel = (ISnoopViewModel) serviceProvider.GetService(typeof(EventsViewModel)); DataContext = this; } } \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SettingsView.xaml b/source/RevitLookup/Views/Pages/SettingsView.xaml similarity index 98% rename from RevitLookup/Views/Pages/SettingsView.xaml rename to source/RevitLookup/Views/Pages/SettingsView.xaml index 06a35026a..43a987a65 100644 --- a/RevitLookup/Views/Pages/SettingsView.xaml +++ b/source/RevitLookup/Views/Pages/SettingsView.xaml @@ -11,7 +11,8 @@ d:DesignHeight="1100" d:DesignWidth="500" d:DataContext="{d:DesignInstance pages:SettingsView}"> - + + - + + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SettingsView.xaml.cs b/source/RevitLookup/Views/Pages/SettingsView.xaml.cs similarity index 100% rename from RevitLookup/Views/Pages/SettingsView.xaml.cs rename to source/RevitLookup/Views/Pages/SettingsView.xaml.cs diff --git a/source/RevitLookup/Views/Pages/SnoopView.xaml b/source/RevitLookup/Views/Pages/SnoopView.xaml new file mode 100644 index 000000000..0aa71609c --- /dev/null +++ b/source/RevitLookup/Views/Pages/SnoopView.xaml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RevitLookup/Views/Pages/SnoopView.xaml.cs b/source/RevitLookup/Views/Pages/SnoopView.xaml.cs similarity index 95% rename from RevitLookup/Views/Pages/SnoopView.xaml.cs rename to source/RevitLookup/Views/Pages/SnoopView.xaml.cs index d10d43056..28cb4c477 100644 --- a/RevitLookup/Views/Pages/SnoopView.xaml.cs +++ b/source/RevitLookup/Views/Pages/SnoopView.xaml.cs @@ -28,14 +28,14 @@ public sealed partial class SnoopView public SnoopView(ISettingsService settingsService, ISnoopViewModel viewModel) : base(settingsService) { InitializeComponent(); + ViewModel = viewModel; DataGridControl = DataGrid; TreeViewControl = TreeView; SearchBoxControl = SearchBox; - TreeView.SelectedItemChanged += OnTreeSelectionChanged; + TreeView.SelectedItemChanged += OnTreeItemSelected; TreeView.ItemsSourceChanged += OnTreeSourceChanged; - ViewModel = viewModel; DataContext = this; } } \ No newline at end of file diff --git a/source/RevitLookup/Views/Resources/Menus.xaml b/source/RevitLookup/Views/Resources/Menus.xaml new file mode 100644 index 000000000..5a36cea1b --- /dev/null +++ b/source/RevitLookup/Views/Resources/Menus.xaml @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/source/RevitLookup/Views/Resources/RevitLookup.Ui.xaml b/source/RevitLookup/Views/Resources/RevitLookup.Ui.xaml new file mode 100644 index 000000000..17a4c1e1f --- /dev/null +++ b/source/RevitLookup/Views/Resources/RevitLookup.Ui.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/RevitLookup/Views/RevitLookupView.xaml b/source/RevitLookup/Views/RevitLookupView.xaml similarity index 65% rename from RevitLookup/Views/RevitLookupView.xaml rename to source/RevitLookup/Views/RevitLookupView.xaml index 5dd2a9217..2e08f3248 100644 --- a/RevitLookup/Views/RevitLookupView.xaml +++ b/source/RevitLookup/Views/RevitLookupView.xaml @@ -7,7 +7,6 @@ xmlns:rl="http://revitlookup.com/xaml" xmlns:pages="clr-namespace:RevitLookup.Views.Pages" xmlns:ml="clr-namespace:RevitLookup.Views.Markup" - xmlns:resources="clr-namespace:RevitLookup.Views.Resources" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="200" @@ -17,15 +16,18 @@ Height="555" ExtendsContentIntoTitleBar="True" Icon="../Resources/Images/ShellIcon.ico"> + - + + + - - - - + TargetPageType="{x:Type pages:DashboardView}" + Icon="{rl:SymbolIcon AppGeneric24}" /> - - - - + TargetPageType="{x:Type pages:SnoopView}" + Icon="{rl:SymbolIcon SlideSearch24}" /> - - - - + TargetPageType="{x:Type pages:EventsView}" + Icon="{rl:SymbolIcon DesktopPulse24}" /> - - - - + TargetPageType="{x:Type pages:SettingsView}" + Icon="{rl:SymbolIcon Settings24}" /> - - - + TargetPageType="{x:Type pages:AboutView}" + Icon="{rl:SymbolIcon Info24}"> + + + @@ -95,4 +86,5 @@ + \ No newline at end of file diff --git a/RevitLookup/Views/RevitLookupView.xaml.cs b/source/RevitLookup/Views/RevitLookupView.xaml.cs similarity index 87% rename from RevitLookup/Views/RevitLookupView.xaml.cs rename to source/RevitLookup/Views/RevitLookupView.xaml.cs index 92cb18c59..69ec992f4 100644 --- a/RevitLookup/Views/RevitLookupView.xaml.cs +++ b/source/RevitLookup/Views/RevitLookupView.xaml.cs @@ -22,7 +22,10 @@ using System.Windows.Input; using CommunityToolkit.Mvvm.Input; using RevitLookup.Services.Contracts; +using RevitLookup.Services.Enums; using Wpf.Ui; +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; namespace RevitLookup.Views; @@ -42,7 +45,8 @@ public RevitLookupView( INavigationService navigationService, IContentDialogService dialogService, ISnackbarService snackbarService, - ISettingsService settingsService) + ISettingsService settingsService, + ISoftwareUpdateService updateService) : this() { _settingsService = settingsService; @@ -56,6 +60,8 @@ public RevitLookupView( snackbarService.DefaultTimeOut = TimeSpan.FromSeconds(3); RestoreSize(settingsService); + SetupBadges(updateService); + ApplicationThemeManager.Apply(_settingsService.Theme, _settingsService.Background); } private void AddShortcuts() @@ -84,6 +90,12 @@ private void RestoreSize(ISettingsService settingsService) EnableSizeTracking(); } + private void SetupBadges(ISoftwareUpdateService updateService) + { + if (updateService.State != SoftwareUpdateState.ReadyToDownload) return; + AboutItemBadge.Visibility = Visibility.Visible; + } + public void EnableSizeTracking() { SizeChanged += OnSizeChanged; diff --git a/RevitLookup/Views/Utils/HelpUtils.cs b/source/RevitLookup/Views/Utils/HelpUtils.cs similarity index 100% rename from RevitLookup/Views/Utils/HelpUtils.cs rename to source/RevitLookup/Views/Utils/HelpUtils.cs diff --git a/RevitLookup/Views/Utils/VisualUtils.cs b/source/RevitLookup/Views/Utils/VisualUtils.cs similarity index 100% rename from RevitLookup/Views/Utils/VisualUtils.cs rename to source/RevitLookup/Views/Utils/VisualUtils.cs