Browse Source

Merge branch 'master' into avalondock-theme

Krzysztof Krysiński 4 years ago
parent
commit
ae9d9e342f

+ 24 - 0
PixiEditor/Helpers/Converters/BoolToBrushConverter.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class BoolToBrushConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            BrushTuple tuple = (BrushTuple)parameter;
+            return (bool)value ? tuple.FirstBrush : tuple.SecondBrush;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 53 - 0
PixiEditor/Helpers/Converters/BrushTuple.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class BrushTuple : NotifyableObject, ITuple
+    {
+        public object this[int index]
+        {
+            get
+            {
+                return index switch
+                {
+                    0 => FirstBrush,
+                    1 => SecondBrush,
+                    _ => throw new IndexOutOfRangeException("Index was out of range")
+                };
+            }
+        }
+
+        private Brush item1;
+
+        public Brush FirstBrush
+        {
+            get => item1;
+            set
+            {
+                item1 = value;
+                RaisePropertyChanged(nameof(FirstBrush));
+            }
+        }
+
+        private Brush item2;
+
+        public Brush SecondBrush
+        {
+            get => item2;
+            set
+            {
+                item2 = value;
+                RaisePropertyChanged(nameof(SecondBrush));
+            }
+        }
+
+        public int Length => 2;
+    }
+}

+ 26 - 0
PixiEditor/Helpers/Converters/EmptyStringToVisibiltyConverter.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    class EmptyStringToVisibiltyConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            string s = (string)value;
+
+            return !string.IsNullOrEmpty(s) ? Visibility.Visible : Visibility.Collapsed;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

BIN
PixiEditor/Images/PixiBotLogo.png


+ 7 - 0
PixiEditor/Models/DataHolders/Document.cs

@@ -25,6 +25,8 @@ namespace PixiEditor.Models.DataHolders
         private int height;
         private int width;
 
+        private DateTime openedUtc = DateTime.UtcNow;
+
         public Document(int width, int height)
         {
             Width = width;
@@ -116,6 +118,11 @@ namespace PixiEditor.Models.DataHolders
             }
         }
 
+        public DateTime OpenedUTC
+        {
+            get => openedUtc;
+        }
+
         private Selection selection = new Selection(Array.Empty<Coordinates>());
 
         public Selection ActiveSelection

+ 21 - 0
PixiEditor/Models/UserPreferences/PreferencesSettings.cs

@@ -55,6 +55,14 @@ namespace PixiEditor.Models.UserPreferences
 
             Preferences[name] = value;
 
+            if (Callbacks.ContainsKey(name))
+            {
+                foreach (var action in Callbacks[name])
+                {
+                    action.Invoke(value);
+                }
+            }
+
             Save();
         }
 
@@ -70,6 +78,19 @@ namespace PixiEditor.Models.UserPreferences
 
 #nullable enable
 
+        public static Dictionary<string, List<Action<object>>> Callbacks { get; set; } = new Dictionary<string, List<Action<object>>>();
+
+        public static void AddCallback(string setting, Action<object> action)
+        {
+            if (Callbacks.ContainsKey(setting))
+            {
+                Callbacks[setting].Add(action);
+                return;
+            }
+
+            Callbacks.Add(setting, new List<Action<object>>() { action });
+        }
+
         public static T? GetPreference<T>(string name)
         {
             return GetPreference(name, default(T));

+ 3 - 0
PixiEditor/PixiEditor.csproj

@@ -39,6 +39,7 @@
     <None Remove="Images\Eye.png" />
     <None Remove="Images\MoveImage.png" />
     <None Remove="Images\MoveViewportImage.png" />
+    <None Remove="Images\PixiBotLogo.png" />
     <None Remove="Images\PixiEditorLogo.png" />
     <None Remove="Images\SelectImage.png" />
     <None Remove="Images\ZoomImage.png" />
@@ -53,6 +54,7 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="Dirkster.AvalonDock" Version="4.50.1" />
+    <PackageReference Include="DiscordRichPresence" Version="1.0.175" />
     <PackageReference Include="Expression.Blend.Sdk">
       <Version>1.0.2</Version>
     </PackageReference>
@@ -78,6 +80,7 @@
     <Resource Include="Images\MoveViewportImage.png" />
     <Resource Include="Images\PenImage.png" />
     <Resource Include="Images\ColorPickerImage.png" />
+    <Resource Include="Images\PixiBotLogo.png" />
     <Resource Include="Images\PixiEditorLogo.png" />
     <Resource Include="Images\RectangleImage.png" />
     <Resource Include="Images\SelectImage.png" />

+ 209 - 0
PixiEditor/ViewModels/SubViewModels/Main/DiscordViewModel.cs

@@ -0,0 +1,209 @@
+using DiscordRPC;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.UserPreferences;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class DiscordViewModel : SubViewModel<ViewModelMain>
+    {
+        private DiscordRpcClient client;
+        private string clientId;
+        private Document currentDocument;
+
+        public bool Enabled
+        {
+            get => client != null;
+            set
+            {
+                if (Enabled != value)
+                {
+                    if (value)
+                    {
+                        Start();
+                    }
+                    else
+                    {
+                        Stop();
+                    }
+                }
+            }
+        }
+
+        private bool showDocumentName = PreferencesSettings.GetPreference(nameof(ShowDocumentName), true);
+
+        public bool ShowDocumentName
+        {
+            get => showDocumentName;
+            set
+            {
+                if (showDocumentName != value)
+                {
+                    showDocumentName = value;
+                    UpdatePresence(currentDocument);
+                }
+            }
+        }
+
+        private bool showDocumentSize = PreferencesSettings.GetPreference(nameof(ShowDocumentSize), true);
+
+        public bool ShowDocumentSize
+        {
+            get => showDocumentSize;
+            set
+            {
+                if (showDocumentSize != value)
+                {
+                    showDocumentSize = value;
+                    UpdatePresence(currentDocument);
+                }
+            }
+        }
+
+        private bool showLayerCount = PreferencesSettings.GetPreference(nameof(ShowLayerCount), true);
+
+        public bool ShowLayerCount
+        {
+            get => showLayerCount;
+            set
+            {
+                if (showLayerCount != value)
+                {
+                    showLayerCount = value;
+                    UpdatePresence(currentDocument);
+                }
+            }
+        }
+
+        public DiscordViewModel(ViewModelMain owner, string clientId)
+            : base(owner)
+        {
+            Owner.BitmapManager.DocumentChanged += DocumentChanged;
+            this.clientId = clientId;
+
+            Enabled = PreferencesSettings.GetPreference<bool>("EnableRichPresence");
+            PreferencesSettings.AddCallback("EnableRichPresence", x => Enabled = (bool)x);
+            PreferencesSettings.AddCallback(nameof(ShowDocumentName), x => ShowDocumentName = (bool)x);
+            PreferencesSettings.AddCallback(nameof(ShowDocumentSize), x => ShowDocumentSize = (bool)x);
+            PreferencesSettings.AddCallback(nameof(ShowLayerCount), x => ShowLayerCount = (bool)x);
+        }
+
+        public void Start()
+        {
+            client = new DiscordRpcClient(clientId);
+            client.OnReady += OnReady;
+            client.Initialize();
+        }
+
+        public void Stop()
+        {
+            client.ClearPresence();
+            client.Dispose();
+            client = null;
+        }
+
+        public void UpdatePresence(Document document)
+        {
+            if (client == null)
+            {
+                return;
+            }
+
+            RichPresence richPresence = NewDefaultRP();
+
+            if (document != null)
+            {
+                richPresence.WithTimestamps(new Timestamps(document.OpenedUTC));
+
+                richPresence.Details = ShowDocumentName ? $"Editing {document.Name}" : "Editing something (incognito)";
+
+                string state = string.Empty;
+
+                if (ShowDocumentSize)
+                {
+                    state = $"{document.Width}x{document.Height}";
+                }
+
+                if (ShowDocumentSize && ShowLayerCount)
+                {
+                    state += ", ";
+                }
+
+                if (ShowLayerCount)
+                {
+                    state += document.Layers.Count == 1 ? "1 Layer" : $"{document.Layers.Count} Layers";
+                }
+
+                richPresence.State = state;
+            }
+
+            client.SetPresence(richPresence);
+        }
+
+        private static RichPresence NewDefaultRP()
+        {
+            return new RichPresence
+            {
+                Details = "Staring at absolutely",
+                State = "nothing",
+
+                Assets = new Assets
+                {
+                    LargeImageKey = "editorlogo",
+                    LargeImageText = "You discovered PixiEditor's logo",
+                    SmallImageKey = "github",
+                    SmallImageText = "Download PixiEditor on GitHub (github.com/PixiEditor/PixiEditor)!"
+                },
+                Timestamps = new Timestamps()
+                {
+                    Start = DateTime.UtcNow
+                }
+            };
+        }
+
+        private void DocumentChanged(object sender, Models.Events.DocumentChangedEventArgs e)
+        {
+            if (currentDocument != null)
+            {
+                currentDocument.PropertyChanged -= DocumentPropertyChanged;
+                currentDocument.LayersChanged -= DocumentLayerChanged;
+            }
+
+            currentDocument = e.NewDocument;
+
+            if (currentDocument != null)
+            {
+                UpdatePresence(currentDocument);
+                currentDocument.PropertyChanged += DocumentPropertyChanged;
+                currentDocument.LayersChanged += DocumentLayerChanged;
+            }
+        }
+
+        private void DocumentLayerChanged(object sender, Models.Controllers.LayersChangedEventArgs e)
+        {
+            UpdatePresence(currentDocument);
+        }
+
+        private void DocumentPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+        {
+            if (e.PropertyName == "Name" || e.PropertyName == "Width" || e.PropertyName == "Height")
+            {
+                UpdatePresence(currentDocument);
+            }
+        }
+
+        private void OnReady(object sender, DiscordRPC.Message.ReadyMessage args)
+        {
+            UpdatePresence(Owner.BitmapManager.ActiveDocument);
+        }
+
+        ~DiscordViewModel()
+        {
+            Stop();
+        }
+    }
+}

+ 4 - 3
PixiEditor/ViewModels/SubViewModels/SubViewModel.cs

@@ -1,12 +1,13 @@
 namespace PixiEditor.ViewModels.SubViewModels
 {
-    public class SubViewModel<T> : ViewModelBase where T: ViewModelBase
+    public class SubViewModel<T> : ViewModelBase
+        where T : ViewModelBase
     {
         public T Owner { get; }
 
         public SubViewModel(T owner)
         {
             Owner = owner;
-        }        
+        }
     }
-}
+}

+ 29 - 0
PixiEditor/ViewModels/SubViewModels/UserPreferences/SettingsGroup.cs

@@ -0,0 +1,29 @@
+using System.ComponentModel;
+using PixiEditor.Helpers;
+using PixiEditor.Models.UserPreferences;
+
+namespace PixiEditor.ViewModels.SubViewModels.UserPreferences
+{
+    public class SettingsGroup : NotifyableObject
+    {
+        protected static T GetPreference<T>(string name)
+        {
+            return PreferencesSettings.GetPreference<T>(name);
+        }
+
+#nullable enable
+
+        protected static T? GetPreference<T>(string name, T? fallbackValue)
+        {
+            return PreferencesSettings.GetPreference(name, fallbackValue);
+        }
+
+#nullable disable
+
+        protected void RaiseAndUpdatePreference<T>(string name, T value)
+        {
+            RaisePropertyChanged(name);
+            PreferencesSettings.UpdatePreference(name, value);
+        }
+    }
+}

+ 89 - 0
PixiEditor/ViewModels/SubViewModels/UserPreferences/SettingsViewModel.cs

@@ -58,6 +58,95 @@ namespace PixiEditor.ViewModels.SubViewModels.UserPreferences
             }
         }
 
+        public class DiscordSettings : SettingsGroup
+        {
+            private bool enableRichPresence = GetPreference(nameof(EnableRichPresence), true);
+
+            public bool EnableRichPresence
+            {
+                get => enableRichPresence;
+                set
+                {
+                    enableRichPresence = value;
+                    RaiseAndUpdatePreference(nameof(EnableRichPresence), value);
+                }
+            }
+
+            private bool showDocumentName = GetPreference(nameof(ShowDocumentName), true);
+
+            public bool ShowDocumentName
+            {
+                get => showDocumentName;
+                set
+                {
+                    showDocumentName = value;
+                    RaiseAndUpdatePreference(nameof(ShowDocumentName), value);
+                    RaisePropertyChanged(nameof(DetailPreview));
+                }
+            }
+
+            private bool showDocumentSize = GetPreference(nameof(ShowDocumentSize), true);
+
+            public bool ShowDocumentSize
+            {
+                get => showDocumentSize;
+                set
+                {
+                    showDocumentSize = value;
+                    RaiseAndUpdatePreference(nameof(ShowDocumentSize), value);
+                    RaisePropertyChanged(nameof(StatePreview));
+                }
+            }
+
+            private bool showLayerCount = GetPreference(nameof(ShowLayerCount), true);
+
+            public bool ShowLayerCount
+            {
+                get => showLayerCount;
+                set
+                {
+                    showLayerCount = value;
+                    RaiseAndUpdatePreference(nameof(ShowLayerCount), value);
+                    RaisePropertyChanged(nameof(StatePreview));
+                }
+            }
+
+            public string DetailPreview
+            {
+                get
+                {
+                    return ShowDocumentName ? $"Editing coolPixelArt.pixi" : "Editing something (incognito)";
+                }
+            }
+
+            public string StatePreview
+            {
+                get
+                {
+                    string state = string.Empty;
+
+                    if (ShowDocumentSize)
+                    {
+                        state = "16x16";
+                    }
+
+                    if (ShowDocumentSize && ShowLayerCount)
+                    {
+                        state += ", ";
+                    }
+
+                    if (ShowLayerCount)
+                    {
+                        state += "2 Layers";
+                    }
+
+                    return state;
+                }
+            }
+        }
+
+        public DiscordSettings Discord { get; set; } = new DiscordSettings();
+
         public void RaiseAndUpdatePreference<T>(string name, T value)
         {
             RaisePropertyChanged(name);

+ 3 - 0
PixiEditor/ViewModels/ViewModelMain.cs

@@ -57,6 +57,8 @@ namespace PixiEditor.ViewModels
 
         public MiscViewModel MiscSubViewModel { get; set; }
 
+        public DiscordViewModel DiscordViewModel { get; set; }
+
         public BitmapManager BitmapManager { get; set; }
 
         public PixelChangesController ChangesController { get; set; }
@@ -89,6 +91,7 @@ namespace PixiEditor.ViewModels
             ColorsSubViewModel = new ColorsViewModel(this);
             DocumentSubViewModel = new DocumentViewModel(this);
             MiscSubViewModel = new MiscViewModel(this);
+            DiscordViewModel = new DiscordViewModel(this, "764168193685979138");
 
             ShortcutController = new ShortcutController
             {

+ 19 - 2
PixiEditor/Views/Dialogs/SettingsWindow.xaml

@@ -3,10 +3,12 @@
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:PixiEditor.Views.Dialogs" xmlns:viewmodels="clr-namespace:PixiEditor.ViewModels" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:views="clr-namespace:PixiEditor.Views" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
+        xmlns:local="clr-namespace:PixiEditor.Views.Dialogs" xmlns:viewmodels="clr-namespace:PixiEditor.ViewModels" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:views="clr-namespace:PixiEditor.Views" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls"
         mc:Ignorable="d"
         Title="Settings" Name="window" 
-        Height="450" Width="800" WindowStyle="None" DataContext="{DynamicResource SettingsWindowViewModel}"
+        Height="600" Width="800"
+        MinHeight="350" MinWidth="600"
+        WindowStyle="None" DataContext="{DynamicResource SettingsWindowViewModel}"
         BorderBrush="Black" BorderThickness="1">
     <Window.Resources>
         <viewmodels:SettingsWindowViewModel x:Key="SettingsWindowViewModel"/>
@@ -46,6 +48,8 @@
                     Command="{Binding SelectCategoryCommand}" CommandParameter="General">General</Button>
             <Button Style="{StaticResource AccentDarkRoundButton}" Margin="10 5 10 5" 
                     Command="{Binding SelectCategoryCommand}" CommandParameter="Updates">Updates</Button>
+            <Button Style="{StaticResource AccentDarkRoundButton}" Margin="10 5 10 5" 
+                    Command="{Binding SelectCategoryCommand}" CommandParameter="Discord">Discord</Button>
         </StackPanel>
         <Grid Grid.Row="1" Grid.Column="1" Background="{StaticResource AccentColor}">
             <Grid Visibility="{Binding SelectedCategory, Converter={StaticResource EqualityBoolToVisibilityConverter},
@@ -74,6 +78,19 @@
                     </StackPanel>
                 </StackPanel>
             </Grid>
+            <Grid Visibility="{Binding SelectedCategory, Converter={StaticResource EqualityBoolToVisibilityConverter},
+            ConverterParameter='Discord'}">
+                <StackPanel Orientation="Vertical">
+                    <Label Style="{StaticResource Header1}" Content="Rich Presence"/>
+                    <StackPanel Orientation="Vertical" Margin="50 0 50 0">
+                        <CheckBox Margin="5" IsChecked="{Binding SettingsSubViewModel.Discord.EnableRichPresence}" Content="Enabled"/>
+                        <CheckBox Margin="5" IsEnabled="{Binding SettingsSubViewModel.Discord.EnableRichPresence}" IsChecked="{Binding SettingsSubViewModel.Discord.ShowDocumentName}" Content="Show Document Name"/>
+                        <CheckBox Margin="5" IsEnabled="{Binding SettingsSubViewModel.Discord.EnableRichPresence}" IsChecked="{Binding SettingsSubViewModel.Discord.ShowDocumentSize}" Content="Show Document Size"/>
+                        <CheckBox Margin="5" IsEnabled="{Binding SettingsSubViewModel.Discord.EnableRichPresence}" IsChecked="{Binding SettingsSubViewModel.Discord.ShowLayerCount}" Content="Show Layer Count"/>
+                        <usercontrols:DiscordRPPreview Margin="5" State="{Binding SettingsSubViewModel.Discord.StatePreview}" Detail="{Binding SettingsSubViewModel.Discord.DetailPreview}" Width="280" IsPlaying="{Binding SettingsSubViewModel.Discord.EnableRichPresence}"/>
+                    </StackPanel>
+                </StackPanel>
+            </Grid>
         </Grid>
     </Grid>
 </Window>

+ 68 - 0
PixiEditor/Views/UserControls/DiscordRPPreview.xaml

@@ -0,0 +1,68 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.DiscordRPPreview"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:PixiEditor.Views.UserControls" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             mc:Ignorable="d" 
+             d:DesignHeight="280" d:DesignWidth="300"
+             x:Name="uc">
+    <UserControl.Resources>
+        <converters:EmptyStringToVisibiltyConverter x:Key="EmptyStringToVisibilty"/>
+        <BoolToVisibilityConverter x:Key="BoolToVisibilty"/>
+        <converters:BoolToBrushConverter x:Key="BoolToBrush"/>
+        <converters:BrushTuple FirstBrush="#7289da" SecondBrush="#202225" x:Key="BackgroundBrushTuple"/>
+        <converters:BrushTuple FirstBrush="White" SecondBrush="#7289da" x:Key="BotLabelTuple"/>
+        <converters:BrushTuple FirstBrush="#7289da" SecondBrush="White" x:Key="BotTextTuple"/>
+        <converters:BrushTuple FirstBrush="White" SecondBrush="#b9bbbe" x:Key="DiscriminatorTuple"/>
+    </UserControl.Resources>
+    <Grid>
+        <Grid.OpacityMask>
+            <VisualBrush Visual="{Binding ElementName=OutsideBorder}"/>
+        </Grid.OpacityMask>
+        <Border CornerRadius="5" Background="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BackgroundBrushTuple}}" x:Name="OutsideBorder"/>
+        <Grid x:Name="background">
+            <Grid.RowDefinitions>
+                    <RowDefinition Height="Auto"/>
+                    <RowDefinition Height="Auto"/>
+                </Grid.RowDefinitions>
+
+                <Grid VerticalAlignment="Center">
+                    <StackPanel>
+                        <Grid  Width="80" Height="80" Margin="20">
+                            <Image Source="{Binding ElementName=uc, Path=UserSource}"/>
+                        <Border Height="30" Width="30" Background="#FF43B581" CornerRadius="90" BorderThickness="5" BorderBrush="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BackgroundBrushTuple}}">
+                                <Border.RenderTransform>
+                                    <TransformGroup>
+                                        <TranslateTransform X="27" Y="27"></TranslateTransform>
+                                    </TransformGroup>
+                                </Border.RenderTransform>
+                            </Border>
+                        </Grid>
+
+                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,15">
+                            <TextBlock Foreground="White" FontSize="16" FontWeight="SemiBold">PixiBot</TextBlock>
+                        <TextBlock Foreground="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource DiscriminatorTuple}}" FontSize="16">#8523</TextBlock>
+                            <Border CornerRadius="3" BorderThickness="1" Background="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BotLabelTuple}}" Margin="5,0,0,0" VerticalAlignment="Center">
+                            <TextBlock Foreground="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToBrush}, ConverterParameter={StaticResource BotTextTuple}}" FontSize="12" Margin="4,2,4,2" FontWeight="Medium">BOT</TextBlock>
+                            </Border>
+                        </StackPanel>
+                    </StackPanel>
+                </Grid>
+                <Grid Grid.Row="1" Background="#0D000000" Visibility="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToVisibilty}}">
+                    <StackPanel Orientation="Vertical" Margin="15">
+                        <TextBlock FontWeight="Bold" FontSize="12" Foreground="White" Margin="0,0,0,10">PLAYING A "GAME"</TextBlock>
+                        <StackPanel Orientation="Horizontal">
+                            <Image Source="../../Images/PixiEditorLogo.png" Height="70"/>
+                            <StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
+                                <TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold">PixiEditor</TextBlock>
+                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, Path=Detail, Converter={StaticResource EmptyStringToVisibilty}}"/>
+                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=State}" Visibility="{Binding ElementName=uc, Path=State, Converter={StaticResource EmptyStringToVisibilty}}"/>
+                            <TextBlock Foreground="White" FontSize="12">00:00 elapsed</TextBlock>
+                            </StackPanel>
+                        </StackPanel>
+                    </StackPanel>
+                </Grid>
+            </Grid>
+        </Grid>
+</UserControl>

+ 67 - 0
PixiEditor/Views/UserControls/DiscordRPPreview.xaml.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace PixiEditor.Views.UserControls
+{
+    /// <summary>
+    /// Interaction logic for DiscordRPPreview.xaml
+    /// </summary>
+    public partial class DiscordRPPreview : UserControl
+    {
+        public static readonly DependencyProperty StateProperty =
+               DependencyProperty.Register(nameof(State), typeof(string), typeof(DiscordRPPreview), new PropertyMetadata("nothing"));
+
+        public string State
+        {
+            get => (string)GetValue(StateProperty);
+            set => SetValue(StateProperty, value);
+        }
+
+        public static readonly DependencyProperty DetailProperty =
+                DependencyProperty.Register(nameof(Detail), typeof(string), typeof(DiscordRPPreview), new PropertyMetadata("Staring at absolutely"));
+
+        public string Detail
+        {
+            get => (string)GetValue(DetailProperty);
+            set => SetValue(DetailProperty, value);
+        }
+
+        public static readonly DependencyProperty UserSourceProperty =
+                DependencyProperty.Register(nameof(UserSource), typeof(string), typeof(DiscordRPPreview), new PropertyMetadata("../../Images/pixiBotLogo.png"));
+
+        public string UserSource
+        {
+            get => (string)GetValue(UserSourceProperty);
+            set => SetValue(UserSourceProperty, value);
+        }
+
+        public static readonly DependencyProperty IsPlayingProperty =
+                DependencyProperty.Register(nameof(IsPlaying), typeof(bool), typeof(DiscordRPPreview), new PropertyMetadata(true));
+
+        public bool IsPlaying
+        {
+            get => (bool)GetValue(IsPlayingProperty);
+            set
+            {
+                SetValue(IsPlayingProperty, value);
+            }
+        }
+
+        public DiscordRPPreview()
+        {
+            InitializeComponent();
+        }
+    }
+}