Skip to content

Commit

Permalink
Add new InfoBar control for displaying notifications to the user
Browse files Browse the repository at this point in the history
  • Loading branch information
ElektroKill committed Sep 17, 2023
1 parent 2cbd254 commit 2defb01
Show file tree
Hide file tree
Showing 16 changed files with 500 additions and 1 deletion.
33 changes: 33 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/App/IAppInfoBar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

namespace dnSpy.Contracts.App {
/// <summary>
/// App info bar
/// </summary>
public interface IAppInfoBar {
/// <summary>
/// Shows a new info bar element of the specific icon with the given message and interactions.
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="icon">The icon of message</param>
/// <param name="interactions">Possible interactions on the element</param>
public IInfoBarElement Show(string message, InfoBarIcon icon = InfoBarIcon.Information, params InfoBarInteraction[] interactions);
}
}
5 changes: 5 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/App/IAppWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public interface IAppWindow {
/// </summary>
IAppStatusBar StatusBar { get; }

/// <summary>
/// Gets the <see cref="IAppInfoBar"/> instance
/// </summary>
IAppInfoBar InfoBar { get; }

/// <summary>
/// true if the app has been loaded
/// </summary>
Expand Down
40 changes: 40 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

namespace dnSpy.Contracts.App {
/// <summary>
/// A single element in the info bar
/// </summary>
public interface IInfoBarElement {
/// <summary>
/// Message being displayed as part of this info bar element
/// </summary>
string Message { get; }

/// <summary>
/// Icon being displayed as part of this info bar element
/// </summary>
InfoBarIcon Icon { get; }

/// <summary>
/// Closes the info bar element
/// </summary>
void Close();
}
}
40 changes: 40 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/App/IInfoBarInteractionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

namespace dnSpy.Contracts.App {
/// <summary>
/// Context passed to an interaction
/// </summary>
public interface IInfoBarInteractionContext {
/// <summary>
/// Interaction text
/// </summary>
string InteractionText { get; }

/// <summary>
/// Closes the <see cref="IInfoBarElement"/> owning this interaction
/// </summary>
void CloseElement();

/// <summary>
/// Removes the interaction from the <see cref="IInfoBarElement"/>
/// </summary>
void RemoveInteraction();
}
}
40 changes: 40 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarIcon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

namespace dnSpy.Contracts.App {
/// <summary>
/// Icon displayed in the info bar
/// </summary>
public enum InfoBarIcon {
/// <summary>
/// Information icon
/// </summary>
Information,

/// <summary>
/// Warning icon
/// </summary>
Warning,

/// <summary>
/// Error icon
/// </summary>
Error
}
}
45 changes: 45 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/App/InfoBarInteraction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

using System;

namespace dnSpy.Contracts.App {
/// <summary>
/// Interaction on an <see cref="IInfoBarElement"/>
/// </summary>
public readonly struct InfoBarInteraction {
/// <summary>
/// Interaction text
/// </summary>
public string Text { get; }

/// <summary>
/// Action to perform when the interaction is clicked
/// </summary>
public Action<IInfoBarInteractionContext> Action { get; }

/// <summary>
/// Creates a new <see cref="InfoBarInteraction"/>
/// </summary>
public InfoBarInteraction(string text, Action<IInfoBarInteractionContext> action) {
Text = text;
Action = action;
}
}
}
5 changes: 5 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/Themes/ColorType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,11 @@ public enum ColorType : uint {
HyperlinkNormal,
HyperlinkMouseOver,
HyperlinkDisabled,
InfoBarBackground,
InfoBarText,
InfoBarInteractionText,
InfoBarCloseButton,
InfoBarCloseButtonHover,

// Add new color types before this comment

Expand Down
42 changes: 42 additions & 0 deletions dnSpy/dnSpy/MainApp/AppInfoBar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

using System.ComponentModel.Composition;
using dnSpy.Contracts.App;
using dnSpy.Controls;

namespace dnSpy.MainApp {
[Export]
sealed class AppInfoBar : IAppInfoBar, IStackedContentChild {
readonly InfoBar infoBar;
readonly InfoBarVM infoBarVM;

public object UIObject => infoBar;

public AppInfoBar() => infoBar = new InfoBar { DataContext = infoBarVM = new InfoBarVM() };

public IInfoBarElement Show(string message, InfoBarIcon icon = InfoBarIcon.Information, params InfoBarInteraction[] interactions) {
var notification = new InfoBarElementVM(infoBarVM, message, icon);
foreach (var interaction in interactions)
notification.AddInteraction(interaction.Text, interaction.Action);
infoBarVM.Elements.Insert(0, notification);
return notification;
}
}
}
7 changes: 6 additions & 1 deletion dnSpy/dnSpy/MainApp/AppWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ sealed class AppWindow : IAppWindow, IDsLoaderContentProvider {
public IAppStatusBar StatusBar => statusBar;
readonly AppStatusBar statusBar;

public IAppInfoBar InfoBar => infoBar;
readonly AppInfoBar infoBar;

Window IAppWindow.MainWindow => mainWindow!;
internal MainWindow MainWindow => mainWindow!;
MainWindow? mainWindow;
Expand Down Expand Up @@ -96,14 +99,15 @@ public void Write() {
readonly MainWindowControl mainWindowControl;

[ImportingConstructor]
AppWindow(ISettingsService settingsService, IDocumentTabService documentTabService, AppToolBar appToolBar, MainWindowControl mainWindowControl, IWpfCommandService wpfCommandService) {
AppWindow(ISettingsService settingsService, IDocumentTabService documentTabService, AppToolBar appToolBar, AppInfoBar infoBar, MainWindowControl mainWindowControl, IWpfCommandService wpfCommandService) {
assemblyInformationalVersion = CalculateAssemblyInformationalVersion(GetType().Assembly);
uiSettings = new UISettings(settingsService);
uiSettings.Read();
stackedContent = new StackedContent<IStackedContentChild>(margin: new Thickness(6));
this.documentTabService = documentTabService;
statusBar = new AppStatusBar();
this.appToolBar = appToolBar;
this.infoBar = infoBar;
this.mainWindowControl = mainWindowControl;
this.wpfCommandService = wpfCommandService;
mainWindowCommands = wpfCommandService.GetCommands(ControlConstants.GUID_MAINWINDOW);
Expand All @@ -124,6 +128,7 @@ static string CalculateAssemblyInformationalVersion(Assembly asm) {
public Window InitializeMainWindow() {
var sc = new StackedContent<IStackedContentChild>(false);
sc.AddChild(appToolBar, StackedContentChildInfo.CreateVertical(new GridLength(0, GridUnitType.Auto)));
sc.AddChild(infoBar, StackedContentChildInfo.CreateVertical(new GridLength(0, GridUnitType.Auto)));
sc.AddChild(stackedContent, StackedContentChildInfo.CreateVertical(new GridLength(1, GridUnitType.Star)));
sc.AddChild(statusBar, StackedContentChildInfo.CreateVertical(new GridLength(0, GridUnitType.Auto)));
mainWindow = new MainWindow(sc.UIObject);
Expand Down
83 changes: 83 additions & 0 deletions dnSpy/dnSpy/MainApp/InfoBar.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!--
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
-->
<UserControl x:Class="dnSpy.MainApp.InfoBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:dnSpy.MainApp"
xmlns:img="clr-namespace:dnSpy.Contracts.Images;assembly=dnSpy.Contracts.DnSpy"
xmlns:p="clr-namespace:dnSpy.Properties;assembly=dnSpy">
<ItemsControl ItemsSource="{Binding Elements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="1 0 1 0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Separator x:Name="topSeparator" Height="1" Margin="0" Visibility="Collapsed" />
<DockPanel Background="{DynamicResource InfoBarBackground}">
<Button x:Name="closeBtn" DockPanel.Dock="Right" Margin="5" Background="Transparent"
BorderBrush="Transparent" ToolTip="{x:Static p:dnSpy_Resources.Window_Close}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}, Path=DataContext.RemoveElementCommand}"
CommandParameter="{Binding}" VerticalAlignment="Center">
<Path x:Name="path" Width="10" Height="8" Stretch="Uniform" Fill="{DynamicResource InfoBarCloseButton}"
Data="F1M0,0L2,0 5,3 8,0 10,0 6,4 10,8 8,8 5,5 2,8 0,8 4,4 0,0z" />
</Button>
<img:DsImage DockPanel.Dock="Left" ImageReference="{Binding Image}" Margin="5" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center" Text="{Binding Message}" Foreground="{DynamicResource InfoBarText}" />
<ItemsControl ItemsSource="{Binding Interactions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="5 0 0 0" Orientation="Horizontal" VerticalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="text" Text="{Binding Text}" VerticalAlignment="Center" Margin="5"
Foreground="{DynamicResource InfoBarInteractionText}">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding ActionCommand}" CommandParameter="{Binding}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
<DataTemplate.Triggers>
<Trigger SourceName="text" Property="IsMouseOver" Value="True">
<Setter TargetName="text" Property="TextBlock.TextDecorations" Value="Underline" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DockPanel>
<Separator Height="1" Margin="0" />
</StackPanel>
<DataTemplate.Triggers>
<Trigger SourceName="closeBtn" Property="IsMouseOver" Value="True">
<Setter TargetName="path" Property="Fill" Value="{DynamicResource InfoBarCloseButtonHover}" />
</Trigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter TargetName="topSeparator" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
Loading

0 comments on commit 2defb01

Please sign in to comment.