Browse Source

Added HelloTherePopup (aka Startup Window), WIP

CPKreuz 4 years ago
parent
commit
8a65b39769

+ 3 - 2
PixiEditor/Models/DataHolders/Document/Document.IO.cs

@@ -1,4 +1,5 @@
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.Linq;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 
 
@@ -53,7 +54,7 @@ namespace PixiEditor.Models.DataHolders
 
 
         private void UpdateRecentlyOpened(string newPath)
         private void UpdateRecentlyOpened(string newPath)
         {
         {
-            ObservableCollection<string> recentlyOpened = XamlAccesibleViewModel.FileSubViewModel.RecentlyOpened;
+            RecentlyOpenedCollection recentlyOpened = XamlAccesibleViewModel.FileSubViewModel.RecentlyOpened;
 
 
             if (!recentlyOpened.Contains(newPath))
             if (!recentlyOpened.Contains(newPath))
             {
             {
@@ -73,7 +74,7 @@ namespace PixiEditor.Models.DataHolders
                 }
                 }
             }
             }
 
 
-            IPreferences.Current.UpdateLocalPreference("RecentlyOpened", recentlyOpened);
+            IPreferences.Current.UpdateLocalPreference("RecentlyOpened", recentlyOpened.Select(x => x.FilePath));
 
 
             XamlAccesibleViewModel.FileSubViewModel.HasRecent = true;
             XamlAccesibleViewModel.FileSubViewModel.HasRecent = true;
         }
         }

+ 59 - 0
PixiEditor/Models/DataHolders/RecentlyOpenedCollection.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.DataHolders
+{
+    public class RecentlyOpenedCollection : ObservableCollection<RecentlyOpenedDocument>
+    {
+        public RecentlyOpenedDocument this[string path]
+        {
+            get
+            {
+                return Get(path);
+            }
+        }
+
+        public RecentlyOpenedCollection()
+        {
+        }
+
+        public RecentlyOpenedCollection(IEnumerable<RecentlyOpenedDocument> documents)
+            : base(documents)
+        {
+        }
+
+        public void Add(string path)
+        {
+            if (string.IsNullOrWhiteSpace(path))
+            {
+                return;
+            }
+
+            Add(Create(path));
+        }
+
+        public bool Contains(string path) => Get(path) is not null;
+
+        public void Remove(string path) => Remove(Get(path));
+
+        public int IndexOf(string path) => IndexOf(Get(path));
+
+        public void Insert(int index, string path)
+        {
+            if (string.IsNullOrWhiteSpace(path))
+            {
+                return;
+            }
+
+            Insert(index, Create(path));
+        }
+
+        private static RecentlyOpenedDocument Create(string path) => new (path);
+
+        private RecentlyOpenedDocument Get(string path) => this.FirstOrDefault(x => x.FilePath == path);
+    }
+}

+ 69 - 0
PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs

@@ -0,0 +1,69 @@
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Windows.Media.Imaging;
+using PixiEditor.Helpers;
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.IO;
+using PixiEditor.Models.Layers;
+using PixiEditor.Parser;
+
+namespace PixiEditor.Models.DataHolders
+{
+    [DebuggerDisplay("{FilePath}")]
+    public class RecentlyOpenedDocument : NotifyableObject
+    {
+        private string filePath;
+
+        private WriteableBitmap previewBitmap;
+
+        public string FilePath
+        {
+            get => filePath;
+            set
+            {
+                SetProperty(ref filePath, value);
+                RaisePropertyChanged(nameof(FileName));
+                PreviewBitmap = null;
+            }
+        }
+
+        public string FileName => Path.GetFileName(filePath);
+
+        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+        public WriteableBitmap PreviewBitmap
+        {
+            get
+            {
+                if (previewBitmap == null)
+                {
+                    PreviewBitmap = LoadPreviewBitmap();
+                }
+
+                return previewBitmap;
+            }
+            private set => SetProperty(ref previewBitmap, value);
+        }
+
+        public RecentlyOpenedDocument(string path)
+        {
+            FilePath = path;
+        }
+
+        private WriteableBitmap LoadPreviewBitmap()
+        {
+            if (FilePath.EndsWith(".pixi"))
+            {
+                SerializableDocument serializableDocument = PixiParser.Deserialize(filePath);
+
+                return BitmapUtils.GeneratePreviewBitmap(serializableDocument.Layers, serializableDocument.Width, serializableDocument.Height, 80, 50);
+            }
+            else
+            {
+                WriteableBitmap bitmap = Importer.ImportImage(FilePath);
+
+                return bitmap;
+            }
+        }
+    }
+}

+ 0 - 60
PixiEditor/Models/DataHolders/SerializableDocument.cs

@@ -1,60 +0,0 @@
-using System;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Windows;
-using System.Windows.Media;
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.Layers;
-
-namespace PixiEditor.Models.DataHolders
-{
-    [Serializable]
-    public class SerializableDocument
-    {
-        public SerializableDocument(Document document)
-        {
-            Width = document.Width;
-            Height = document.Height;
-            Layers = document.Layers.Select(x => new SerializableLayer(x)).ToArray();
-            Swatches = document.Swatches.Select(x => new Tuple<byte, byte, byte, byte>(x.A, x.R, x.G, x.B)).ToArray();
-        }
-
-        public int Width { get; set; }
-
-        public int Height { get; set; }
-
-        public SerializableLayer[] Layers { get; set; }
-
-        public Tuple<byte, byte, byte, byte>[] Swatches { get; set; }
-
-        public Document ToDocument()
-        {
-            Document document = new Document(Width, Height)
-            {
-                Layers = ToLayers(),
-                Swatches = new ObservableCollection<Color>(Swatches.Select(x =>
-                    Color.FromArgb(x.Item1, x.Item2, x.Item3, x.Item4)))
-            };
-            return document;
-        }
-
-        public ObservableCollection<Layer> ToLayers()
-        {
-            ObservableCollection<Layer> layers = new ObservableCollection<Layer>();
-            for (int i = 0; i < Layers.Length; i++)
-            {
-                SerializableLayer serLayer = Layers[i];
-                Layer layer =
-                    new Layer(serLayer.Name, BitmapUtils.BytesToWriteableBitmap(serLayer.Width, serLayer.Height, serLayer.BitmapBytes))
-                    {
-                        IsVisible = serLayer.IsVisible,
-                        Offset = new Thickness(serLayer.OffsetX, serLayer.OffsetY, 0, 0),
-                        Opacity = serLayer.Opacity
-                    };
-                layers.Add(layer);
-            }
-
-            return layers;
-        }
-    }
-}

+ 20 - 0
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -7,6 +7,7 @@ using System.Windows.Media.Imaging;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
+using PixiEditor.Parser;
 
 
 namespace PixiEditor.Models.ImageManipulation
 namespace PixiEditor.Models.ImageManipulation
 {
 {
@@ -106,6 +107,25 @@ namespace PixiEditor.Models.ImageManipulation
             return previewBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
             return previewBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
         }
         }
 
 
+        public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<SerializableLayer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
+        {
+            WriteableBitmap previewBitmap = BitmapFactory.New(width, height);
+
+            // 0.8 because blit doesn't take into consideration layer opacity. Small images with opacity > 80% are simillar enough.
+            foreach (var layer in layers.Where(x => x.IsVisible && x.Opacity > 0.8f))
+            {
+                previewBitmap.Blit(
+                    new Rect(layer.OffsetX, layer.OffsetY, layer.Width, layer.Height),
+                    BitmapUtils.BytesToWriteableBitmap(layer.Width, layer.Height, layer.BitmapBytes),
+                    new Rect(0, 0, layer.Width, layer.Height));
+            }
+
+            int resizeWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
+            int resizeHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
+
+            return previewBitmap.Resize(resizeWidth, resizeHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+        }
+
         public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
         public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
         {
         {
             Dictionary<Guid, Color[]> result = new ();
             Dictionary<Guid, Color[]> result = new ();

+ 0 - 77
PixiEditor/Models/Layers/SerializableLayer.cs

@@ -1,77 +0,0 @@
-using System;
-using System.Linq;
-
-namespace PixiEditor.Models.Layers
-{
-    [Serializable]
-    public class SerializableLayer
-    {
-        public SerializableLayer(Layer layer)
-        {
-            Name = layer.Name;
-            Width = layer.Width;
-            Height = layer.Height;
-            BitmapBytes = layer.ConvertBitmapToBytes();
-            IsVisible = layer.IsVisible;
-            OffsetX = (int)layer.Offset.Left;
-            OffsetY = (int)layer.Offset.Top;
-            Opacity = layer.Opacity;
-            MaxWidth = layer.MaxWidth;
-            MaxHeight = layer.MaxHeight;
-        }
-
-        public string Name { get; set; }
-
-        public int Width { get; set; }
-
-        public int Height { get; set; }
-
-        public int MaxWidth { get; set; }
-
-        public int MaxHeight { get; set; }
-
-        public byte[] BitmapBytes { get; set; }
-
-        public bool IsVisible { get; set; }
-
-        public int OffsetX { get; set; }
-
-        public int OffsetY { get; set; }
-
-        public float Opacity { get; set; }
-
-        public override bool Equals(object obj)
-        {
-            if (obj == null || obj.GetType() != typeof(SerializableLayer))
-            {
-                return false;
-            }
-
-            SerializableLayer layer = (SerializableLayer)obj;
-
-            return Equals(layer);
-        }
-
-        public override int GetHashCode()
-        {
-            HashCode hashCode = default(HashCode);
-            hashCode.Add(Name);
-            hashCode.Add(Width);
-            hashCode.Add(Height);
-            hashCode.Add(MaxWidth);
-            hashCode.Add(MaxHeight);
-            hashCode.Add(BitmapBytes);
-            hashCode.Add(IsVisible);
-            hashCode.Add(OffsetX);
-            hashCode.Add(OffsetY);
-            hashCode.Add(Opacity);
-            return hashCode.ToHashCode();
-        }
-
-        protected bool Equals(SerializableLayer other)
-        {
-            return Name == other.Name && Width == other.Width && Height == other.Height && MaxWidth == other.MaxWidth && MaxHeight == other.MaxHeight &&
-                   BitmapBytes.SequenceEqual(other.BitmapBytes) && IsVisible == other.IsVisible && OffsetX == other.OffsetX && OffsetY == other.OffsetY && Opacity.Equals(other.Opacity);
-        }
-    }
-}

+ 34 - 5
PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -16,6 +16,7 @@ using PixiEditor.Models.Enums;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
+using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
@@ -43,7 +44,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
         }
         }
 
 
-        public ObservableCollection<string> RecentlyOpened { get; set; } = new ObservableCollection<string>();
+        public RecentlyOpenedCollection RecentlyOpened { get; set; } = new RecentlyOpenedCollection();
 
 
         public FileViewModel(ViewModelMain owner)
         public FileViewModel(ViewModelMain owner)
             : base(owner)
             : base(owner)
@@ -54,7 +55,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             ExportFileCommand = new RelayCommand(ExportFile, CanSave);
             ExportFileCommand = new RelayCommand(ExportFile, CanSave);
             OpenRecentCommand = new RelayCommand(OpenRecent);
             OpenRecentCommand = new RelayCommand(OpenRecent);
             Owner.OnStartupEvent += Owner_OnStartupEvent;
             Owner.OnStartupEvent += Owner_OnStartupEvent;
-            RecentlyOpened = new ObservableCollection<string>(IPreferences.Current.GetLocalPreference<JArray>(nameof(RecentlyOpened), new JArray()).ToObject<string[]>());
+            RecentlyOpened = new RecentlyOpenedCollection(GetRecentlyOpenedDocuments());
 
 
             if (RecentlyOpened.Count > 0)
             if (RecentlyOpened.Count > 0)
             {
             {
@@ -98,6 +99,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
         }
         }
 
 
+        public void OpenHelloTherePopup()
+        {
+            new HelloTherePopup(this).Show();
+        }
+
         public void NewDocument(int width, int height, bool addBaseLayer = true)
         public void NewDocument(int width, int height, bool addBaseLayer = true)
         {
         {
             Owner.BitmapManager.Documents.Add(new Document(width, height));
             Owner.BitmapManager.Documents.Add(new Document(width, height));
@@ -138,6 +144,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             SaveDocument(parameter: asNew ? "asnew" : null);
             SaveDocument(parameter: asNew ? "asnew" : null);
         }
         }
 
 
+        public void OpenAny()
+        {
+            Open((object)null);
+        }
+
         private void Owner_OnStartupEvent(object sender, System.EventArgs e)
         private void Owner_OnStartupEvent(object sender, System.EventArgs e)
         {
         {
             var lastArg = Environment.GetCommandLineArgs().Last();
             var lastArg = Environment.GetCommandLineArgs().Last();
@@ -147,9 +158,9 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
             else
             else
             {
             {
-                if (IPreferences.Current.GetPreference("ShowNewFilePopupOnStartup", true))
+                if (IPreferences.Current.GetPreference("ShowStartupWindow", true))
                 {
                 {
-                    OpenNewFilePopup(null);
+                    OpenHelloTherePopup();
                 }
                 }
             }
             }
         }
         }
@@ -195,9 +206,13 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         {
         {
             OpenFileDialog dialog = new OpenFileDialog
             OpenFileDialog dialog = new OpenFileDialog
             {
             {
-                Filter = "All Files|*.*|PixiEditor Files | *.pixi|PNG Files|*.png",
+                Filter =
+                "Any|*.pixi;*.png;*.jpg;*.jpeg;|" +
+                "PixiEditor Files | *.pixi|" +
+                "Image Files|*.png;*.jpg;*.jpeg;",
                 DefaultExt = "pixi"
                 DefaultExt = "pixi"
             };
             };
+
             if ((bool)dialog.ShowDialog())
             if ((bool)dialog.ShowDialog())
             {
             {
                 if (Importer.IsSupportedFile(dialog.FileName))
                 if (Importer.IsSupportedFile(dialog.FileName))
@@ -270,5 +285,19 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         {
         {
             return Owner.BitmapManager.ActiveDocument != null;
             return Owner.BitmapManager.ActiveDocument != null;
         }
         }
+
+        private List<RecentlyOpenedDocument> GetRecentlyOpenedDocuments()
+        {
+            var paths = IPreferences.Current.GetLocalPreference(nameof(RecentlyOpened), new JArray()).ToObject<string[]>();
+
+            List<RecentlyOpenedDocument> documents = new List<RecentlyOpenedDocument>();
+
+            foreach (string path in paths)
+            {
+                documents.Add(new RecentlyOpenedDocument(path));
+            }
+
+            return documents;
+        }
     }
     }
 }
 }

+ 4 - 9
PixiEditor/ViewModels/SubViewModels/UserPreferences/Settings/FileSettings.cs

@@ -8,17 +8,12 @@ namespace PixiEditor.ViewModels.SubViewModels.UserPreferences.Settings
 {
 {
     public class FileSettings : SettingsGroup
     public class FileSettings : SettingsGroup
     {
     {
-        private bool showNewFilePopupOnStartup = GetPreference("ShowNewFilePopupOnStartup", true);
+        private bool showStartupWindow = GetPreference(nameof(ShowStartupWindow), true);
 
 
-        public bool ShowNewFilePopupOnStartup
+        public bool ShowStartupWindow
         {
         {
-            get => showNewFilePopupOnStartup;
-            set
-            {
-                showNewFilePopupOnStartup = value;
-                string name = nameof(ShowNewFilePopupOnStartup);
-                RaiseAndUpdatePreference(name, value);
-            }
+            get => showStartupWindow;
+            set => RaiseAndUpdatePreference(ref showStartupWindow, value);
         }
         }
 
 
         private long defaultNewFileWidth = GetPreference("DefaultNewFileWidth", 16L);
         private long defaultNewFileWidth = GetPreference("DefaultNewFileWidth", 16L);

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

@@ -1,4 +1,5 @@
 using System.ComponentModel;
 using System.ComponentModel;
+using System.Runtime.CompilerServices;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 
 
@@ -25,5 +26,11 @@ namespace PixiEditor.ViewModels.SubViewModels.UserPreferences
             RaisePropertyChanged(name);
             RaisePropertyChanged(name);
             IPreferences.Current.UpdatePreference(name, value);
             IPreferences.Current.UpdatePreference(name, value);
         }
         }
+
+        protected void RaiseAndUpdatePreference<T>(ref T backingStore, T value, [CallerMemberName]string name = "")
+        {
+            SetProperty(ref backingStore, value, name);
+            IPreferences.Current.UpdatePreference(name, value);
+        }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/ViewModels/ViewModelMain.cs

@@ -17,6 +17,7 @@ using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
+using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.ViewModels
 namespace PixiEditor.ViewModels
 {
 {
@@ -254,7 +255,6 @@ namespace PixiEditor.ViewModels
             {
             {
                 return false;
                 return false;
             }
             }
-
         }
         }
 
 
         private void OnStartup(object parameter)
         private void OnStartup(object parameter)

+ 119 - 0
PixiEditor/Views/Dialogs/HelloTherePopup.xaml

@@ -0,0 +1,119 @@
+<Window x:Class="PixiEditor.Views.Dialogs.HelloTherePopup"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:dataHolders="clr-namespace:PixiEditor.Models.DataHolders" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+        xmlns:sys="clr-namespace:System;assembly=System.Runtime"
+        mc:Ignorable="d"
+        Title="Hello there!" Height="630" Width="545"
+        WindowStyle="None" WindowStartupLocation="CenterScreen">
+
+    <Window.Resources>
+        <converters:EqualityBoolToVisibilityConverter x:Key="EqualBoolToVisibilty"/>
+
+        <Style TargetType="TextBlock">
+            <Setter Property="Foreground" Value="White"/>
+            <Setter Property="FontSize" Value="16"/>
+        </Style>
+    </Window.Resources>
+
+    <WindowChrome.WindowChrome>
+        <WindowChrome CaptionHeight="35"
+                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}"/>
+    </WindowChrome.WindowChrome>
+
+    <Window.CommandBindings>
+        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Close" />
+    </Window.CommandBindings>
+
+    <Grid Background="{StaticResource AccentColor}">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="35" />
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+
+        <DockPanel Grid.Row="0" Background="{StaticResource MainColor}">
+            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Style="{StaticResource CloseButtonStyle}"
+                    WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
+                    Command="{x:Static SystemCommands.CloseWindowCommand}" />
+        </DockPanel>
+
+        <ScrollViewer Grid.Row="1">
+            <Grid Grid.Row="1" Margin="0,30,0,0">
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="90"/>
+                    <RowDefinition Height="Auto"/>
+                    <RowDefinition MinHeight="120"/>
+                    <RowDefinition Height="Auto"/>
+                </Grid.RowDefinitions>
+
+                <StackPanel HorizontalAlignment="Center">
+                    <StackPanel Orientation="Horizontal">
+                        <Image Source="../../Images/PixiEditorLogo.png" Height="40" VerticalAlignment="Center"/>
+                        <TextBlock FontSize="40" FontWeight="SemiBold" VerticalAlignment="Center" Margin="10,0,0,0">PixiEditor</TextBlock>
+                    </StackPanel>
+                    <TextBlock HorizontalAlignment="Center" FontSize="20" FontWeight="Medium">v0.2</TextBlock>
+                </StackPanel>
+
+                <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
+                    <Button Style="{StaticResource DarkRoundButton}" Command="{Binding OpenFileCommand}" Width="150" Margin="10">Open</Button>
+                    <Button Style="{StaticResource DarkRoundButton}" Command="{Binding OpenNewFileCommand}" Width="150" Margin="10">New</Button>
+                </StackPanel>
+
+                <StackPanel Grid.Row="2" HorizontalAlignment="Center" Margin="0,30,0,0">
+                    <TextBlock FontSize="23" FontWeight="SemiBold" HorizontalAlignment="Center">Recent Files</TextBlock>
+                    <TextBlock Margin="0,12.5,0,0" Foreground="LightGray" HorizontalAlignment="Center">
+                        <TextBlock.Visibility>
+                            <Binding Path="RecentlyOpened.Count"
+                                     Converter="{StaticResource EqualBoolToVisibilty}">
+                                <Binding.ConverterParameter>
+                                    <sys:Int32/>
+                                </Binding.ConverterParameter>
+                            </Binding>
+                        </TextBlock.Visibility>
+                        Such empty here
+                    </TextBlock>
+                    <ItemsControl ItemsSource="{Binding RecentlyOpened}">
+                        <ItemsControl.ItemTemplate>
+                            <DataTemplate DataType="{x:Type dataHolders:RecentlyOpenedDocument}">
+                                <StackPanel Margin="8,5,8,0" ToolTip="{Binding FilePath}">
+                                    <Button Style="{StaticResource DarkRoundButton}" Margin="0,10,0,0" HorizontalAlignment="Center"
+                                            Command="{Binding DataContext.OpenRecentCommand, RelativeSource={RelativeSource AncestorType=WrapPanel}}" CommandParameter="{Binding FilePath}">
+                                        <Image Source="{Binding PreviewBitmap}" Height="50" Margin="20"/>
+                                    </Button>
+
+                                    <TextBlock Text="{Binding FileName}" FontSize="18" Margin="10,2,10,2" HorizontalAlignment="Center" Foreground="White"/>
+                                </StackPanel>
+                            </DataTemplate>
+                        </ItemsControl.ItemTemplate>
+                        <ItemsControl.ItemsPanel>
+                            <ItemsPanelTemplate>
+                                <WrapPanel HorizontalAlignment="Center"/>
+                            </ItemsPanelTemplate>
+                        </ItemsControl.ItemsPanel>
+                    </ItemsControl>
+                </StackPanel>
+
+                <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,15">
+                    <StackPanel.Style>
+                        <Style TargetType="StackPanel">
+                            <Style.Resources>
+                                <Style TargetType="Button" BasedOn="{StaticResource DarkRoundButton}">
+                                    <Setter Property="Width" Value="150"/>
+                                    <Setter Property="Margin" Value="5,0,5,0"/>
+                                    <Setter Property="FontSize" Value="18"/>
+                                    <Setter Property="Height" Value="28"/>
+                                </Style>
+                            </Style.Resources>
+                        </Style>
+                    </StackPanel.Style>
+                    <Button Command="{Binding OpenHyperlinkCommand}" CommandParameter="https://discord.gg/tzkQFDkqQS">Discord</Button>
+                    <Button Command="{Binding OpenHyperlinkCommand}" CommandParameter="https://reddit.com/r/PixiEditor">r/PixiEditor</Button>
+                    <Button Command="{Binding OpenHyperlinkCommand}" CommandParameter="https://github.com/PixiEditor/PixiEditor">GitHub</Button>
+                </StackPanel>
+            </Grid>
+        </ScrollViewer>
+    </Grid>
+</Window>

+ 104 - 0
PixiEditor/Views/Dialogs/HelloTherePopup.xaml.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using Newtonsoft.Json.Linq;
+using PixiEditor.Helpers;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
+using PixiEditor.Models.IO;
+using PixiEditor.Models.UserPreferences;
+using PixiEditor.ViewModels.SubViewModels.Main;
+
+namespace PixiEditor.Views.Dialogs
+{
+    /// <summary>
+    /// Interaction logic for HelloTherePopup.xaml.
+    /// </summary>
+    public partial class HelloTherePopup : Window
+    {
+        public RecentlyOpenedCollection RecentlyOpened { get => FileViewModel.RecentlyOpened; }
+
+        public static readonly DependencyProperty FileViewModelProperty =
+            DependencyProperty.Register(nameof(FileViewModel), typeof(FileViewModel), typeof(HelloTherePopup));
+
+        public FileViewModel FileViewModel { get => (FileViewModel)GetValue(FileViewModelProperty); set => SetValue(FileViewModelProperty, value); }
+
+        public static readonly DependencyProperty RecentlyOpenedEmptyProperty =
+            DependencyProperty.Register(nameof(RecentlyOpenedEmpty), typeof(bool), typeof(HelloTherePopup));
+
+        public bool RecentlyOpenedEmpty { get => (bool)GetValue(RecentlyOpenedEmptyProperty); set => SetValue(RecentlyOpenedEmptyProperty, value); }
+
+        public RelayCommand OpenFileCommand { get; set; }
+
+        public RelayCommand OpenNewFileCommand { get; set; }
+
+        public RelayCommand OpenRecentCommand { get; set; }
+
+        public RelayCommand OpenHyperlinkCommand { get => FileViewModel.Owner.MiscSubViewModel.OpenHyperlinkCommand; }
+
+        public HelloTherePopup(FileViewModel fileViewModel)
+        {
+            DataContext = this;
+            FileViewModel = fileViewModel;
+
+            OpenFileCommand = new RelayCommand(OpenFile);
+            OpenNewFileCommand = new RelayCommand(OpenNewFile);
+            OpenRecentCommand = new RelayCommand(OpenRecent);
+
+            RecentlyOpenedEmpty = RecentlyOpened.Count == 0;
+            RecentlyOpened.CollectionChanged += RecentlyOpened_CollectionChanged;
+
+            InitializeComponent();
+        }
+
+        protected override void OnDeactivated(EventArgs e)
+        {
+            CloseIfRelease();
+        }
+
+        private void RecentlyOpened_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+        {
+            RecentlyOpenedEmpty = FileViewModel.RecentlyOpened.Count == 0;
+        }
+
+        [Conditional("RELEASE")]
+        private void CloseIfRelease()
+        {
+            Close();
+        }
+
+        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+        {
+            e.CanExecute = true;
+        }
+
+        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+        {
+            SystemCommands.CloseWindow(this);
+        }
+
+        private void OpenFile(object parameter)
+        {
+            Close();
+            FileViewModel.OpenAny();
+        }
+
+        private void OpenNewFile(object parameter)
+        {
+            Close();
+            FileViewModel.OpenNewFilePopup(parameter);
+        }
+
+        private void OpenRecent(object parameter)
+        {
+            FileViewModel.OpenRecent(parameter);
+            Close();
+        }
+    }
+}

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

@@ -57,8 +57,8 @@
                 <StackPanel Orientation="Vertical">
                 <StackPanel Orientation="Vertical">
                     <Label Content="File" Style="{StaticResource Header1}"/>
                     <Label Content="File" Style="{StaticResource Header1}"/>
                     <StackPanel Orientation="Vertical" Margin="50 0 50 0">
                     <StackPanel Orientation="Vertical" Margin="50 0 50 0">
-                        <CheckBox Content="Show New File dialog on startup" 
-                                  IsChecked="{Binding SettingsSubViewModel.File.ShowNewFilePopupOnStartup}"/>
+                        <CheckBox Content="Show Startup Window" 
+                                  IsChecked="{Binding SettingsSubViewModel.File.ShowStartupWindow}"/>
                         <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                         <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                             <Label Content="Max Saved Opened Recently:" ToolTip="How many documents are shown under File > Recent. Default: 10" Style="{StaticResource BaseLabel}"/>
                             <Label Content="Max Saved Opened Recently:" ToolTip="How many documents are shown under File > Recent. Default: 10" Style="{StaticResource BaseLabel}"/>
                             <views:NumberInput FontSize="16" Value="{Binding SettingsSubViewModel.File.MaxOpenedRecently}" Width="40"/>
                             <views:NumberInput FontSize="16" Value="{Binding SettingsSubViewModel.File.MaxOpenedRecently}" Width="40"/>

+ 8 - 3
PixiEditor/Views/MainWindow.xaml

@@ -11,7 +11,7 @@
         xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
         xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
-        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}"
+        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}" xmlns:dataHolders="clr-namespace:PixiEditor.Models.DataHolders"
         mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
         mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         WindowStartupLocation="CenterScreen" WindowState="Maximized">
         WindowStartupLocation="CenterScreen" WindowState="Maximized">
@@ -86,11 +86,16 @@
                     <MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="{Binding FileSubViewModel.OpenFileCommand}" />
                     <MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="{Binding FileSubViewModel.OpenFileCommand}" />
                     <MenuItem Header="_Recent" ItemsSource="{Binding FileSubViewModel.RecentlyOpened}" x:Name="recentItemMenu" IsEnabled="{Binding FileSubViewModel.HasRecent}">
                     <MenuItem Header="_Recent" ItemsSource="{Binding FileSubViewModel.RecentlyOpened}" x:Name="recentItemMenu" IsEnabled="{Binding FileSubViewModel.HasRecent}">
                         <MenuItem.ItemContainerStyle>
                         <MenuItem.ItemContainerStyle>
-                            <Style TargetType="MenuItem" BasedOn="{StaticResource menuItemStyle}">
+                            <Style TargetType="MenuItem">
                                 <Setter Property="Command" Value="{Binding ElementName=recentItemMenu, Path=DataContext.FileSubViewModel.OpenRecentCommand}"/>
                                 <Setter Property="Command" Value="{Binding ElementName=recentItemMenu, Path=DataContext.FileSubViewModel.OpenRecentCommand}"/>
-                                <Setter Property="CommandParameter" Value="{Binding}"/>
+                                <Setter Property="CommandParameter" Value="{Binding FilePath}"/>
                             </Style>
                             </Style>
                         </MenuItem.ItemContainerStyle>
                         </MenuItem.ItemContainerStyle>
+                        <MenuItem.ItemTemplate>
+                            <DataTemplate DataType="{x:Type dataHolders:RecentlyOpenedDocument}">
+                                <TextBlock Text="{Binding FilePath}"/>
+                            </DataTemplate>
+                        </MenuItem.ItemTemplate>
                     </MenuItem>
                     </MenuItem>
                     <MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="{Binding FileSubViewModel.SaveDocumentCommand}" />
                     <MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="{Binding FileSubViewModel.SaveDocumentCommand}" />
                     <MenuItem Header="_Save As..." InputGestureText="Ctrl+Shift+S"
                     <MenuItem Header="_Save As..." InputGestureText="Ctrl+Shift+S"