diff --git a/SukiUI.Demo/Assets/Icons/Mail/icons8-draft-96.png b/SukiUI.Demo/Assets/Icons/Mail/icons8-draft-96.png
new file mode 100644
index 000000000..c9f341307
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/Mail/icons8-draft-96.png differ
diff --git a/SukiUI.Demo/Assets/Icons/Mail/icons8-female-user-96.png b/SukiUI.Demo/Assets/Icons/Mail/icons8-female-user-96.png
new file mode 100644
index 000000000..618426e3f
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/Mail/icons8-female-user-96.png differ
diff --git a/SukiUI.Demo/Assets/Icons/Mail/icons8-folder-96.png b/SukiUI.Demo/Assets/Icons/Mail/icons8-folder-96.png
new file mode 100644
index 000000000..b0c9232f1
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/Mail/icons8-folder-96.png differ
diff --git a/SukiUI.Demo/Assets/Icons/Mail/icons8-mailbox-96.png b/SukiUI.Demo/Assets/Icons/Mail/icons8-mailbox-96.png
new file mode 100644
index 000000000..a10e02414
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/Mail/icons8-mailbox-96.png differ
diff --git a/SukiUI.Demo/Assets/Icons/Mail/icons8-trash-96.png b/SukiUI.Demo/Assets/Icons/Mail/icons8-trash-96.png
new file mode 100644
index 000000000..379202991
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/Mail/icons8-trash-96.png differ
diff --git a/SukiUI.Demo/Assets/Icons/Mail/icons8-user-male-96.png b/SukiUI.Demo/Assets/Icons/Mail/icons8-user-male-96.png
new file mode 100644
index 000000000..824a5fbf1
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/Mail/icons8-user-male-96.png differ
diff --git a/SukiUI.Demo/Assets/Icons/mail.png b/SukiUI.Demo/Assets/Icons/mail.png
new file mode 100644
index 000000000..817fb2a13
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/mail.png differ
diff --git a/SukiUI.Demo/Assets/Icons/notes.png b/SukiUI.Demo/Assets/Icons/notes.png
new file mode 100644
index 000000000..3dd46a169
Binary files /dev/null and b/SukiUI.Demo/Assets/Icons/notes.png differ
diff --git a/SukiUI.Demo/Assets/desktopbackground.jpg b/SukiUI.Demo/Assets/desktopbackground.jpg
new file mode 100644
index 000000000..79acee494
Binary files /dev/null and b/SukiUI.Demo/Assets/desktopbackground.jpg differ
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/MailApp.axaml b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/MailApp.axaml
new file mode 100644
index 000000000..423769c44
--- /dev/null
+++ b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/MailApp.axaml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hi, let's have a meeting tomorrow to discuss the project. I've been reviewing the project details and have some ideas I'd like to share. It's crucial that we align on our next steps to ensure the project's success.
+
+ Please come prepared with any questions or insights you may have. Looking forward to our meeting!
+
+ Best regards, William
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/MailApp.axaml.cs b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/MailApp.axaml.cs
new file mode 100644
index 000000000..33b5078e2
--- /dev/null
+++ b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/MailApp.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace SukiUI.Demo.Features.ControlsLibrary.ExperimentalControls.Apps
+{
+ public partial class MailApp : UserControl
+ {
+ public MailApp()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/Notes.axaml b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/Notes.axaml
new file mode 100644
index 000000000..fbe5638a3
--- /dev/null
+++ b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/Notes.axaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+ 0.05
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/Notes.axaml.cs b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/Notes.axaml.cs
new file mode 100644
index 000000000..c07af395e
--- /dev/null
+++ b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/Apps/Notes.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace SukiUI.Demo.Features.ControlsLibrary.ExperimentalControls.Apps
+{
+ public partial class Notes : UserControl
+ {
+ public Notes()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalView.axaml b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalView.axaml
new file mode 100644
index 000000000..7529f42ff
--- /dev/null
+++ b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalView.axaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalView.axaml.cs b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalView.axaml.cs
similarity index 100%
rename from SukiUI.Demo/Features/ControlsLibrary/ExperimentalView.axaml.cs
rename to SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalView.axaml.cs
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalViewModel.cs b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalViewModel.cs
similarity index 100%
rename from SukiUI.Demo/Features/ControlsLibrary/ExperimentalViewModel.cs
rename to SukiUI.Demo/Features/ControlsLibrary/ExperimentalControls/ExperimentalViewModel.cs
diff --git a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalView.axaml b/SukiUI.Demo/Features/ControlsLibrary/ExperimentalView.axaml
deleted file mode 100644
index 4ed15c9bd..000000000
--- a/SukiUI.Demo/Features/ControlsLibrary/ExperimentalView.axaml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
- This page contains some experimental controls. These are not officially supported.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/SukiUI.Demo/SukiUI.Demo.csproj b/SukiUI.Demo/SukiUI.Demo.csproj
index 84a83663b..634c21596 100644
--- a/SukiUI.Demo/SukiUI.Demo.csproj
+++ b/SukiUI.Demo/SukiUI.Demo.csproj
@@ -44,6 +44,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -60,4 +80,11 @@
+
+
+ ExperimentalView.axaml
+ Code
+
+
+
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/InternalWindow.axaml b/SukiUI/Controls/Experimental/DesktopEnvironment/InternalWindow.axaml
new file mode 100644
index 000000000..2ba964fe5
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/InternalWindow.axaml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/InternalWindow.axaml.cs b/SukiUI/Controls/Experimental/DesktopEnvironment/InternalWindow.axaml.cs
new file mode 100644
index 000000000..8f8ff72a2
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/InternalWindow.axaml.cs
@@ -0,0 +1,193 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace SukiUI.Controls.Experimental.DesktopEnvironment
+{
+ public partial class InternalWindow : UserControl
+ {
+ private Point _startPoint;
+ private Thickness _startMargin;
+ private bool _isDragging;
+ private Panel _windowBorder;
+ private Size _initialSize;
+ private WindowManager _ParentWM;
+ private bool _isResizing;
+
+ private bool isVisible = true;
+ public InternalWindow(WindowManager WM, Control content, string title)
+ {
+ InitializeComponent();
+
+ _ParentWM = WM;
+ this.Get("TBTitle").Text = title;
+ _windowBorder = this.FindControl("WindowBorder");
+ this.FindControl("WindowContent").Content = content;
+
+ // Gérer les événements pour permettre le déplacement
+ var titleBar = this.FindControl("PART_TitleBarBackground");
+ titleBar.PointerPressed += OnTitleBarPointerPressed;
+ titleBar.PointerMoved += OnTitleBarPointerMoved;
+ titleBar.PointerReleased += OnTitleBarPointerReleased;
+ }
+
+ private void OnTitleBarPointerPressed(object sender, PointerPressedEventArgs e)
+ {
+
+ _startPoint = e.GetPosition( _ParentWM);
+ _startMargin = _windowBorder.Margin;
+ _isDragging = true;
+
+ }
+
+ private void OnTitleBarPointerMoved(object sender, PointerEventArgs e)
+ {
+ if (_isDragging)
+ {
+ var currentPosition = e.GetPosition( _ParentWM);
+
+ // Calculer le décalage par rapport à la position initiale
+ var offsetX = currentPosition.X - _startPoint.X;
+ var offsetY = currentPosition.Y - _startPoint.Y;
+
+ // Appliquer la transformation de déplacement à la fenêtre
+ _windowBorder.Margin = new Thickness(_startMargin.Left + offsetX, _startMargin.Top + offsetY, 0, 0);
+
+ }
+ }
+
+ private void OnTitleBarPointerReleased(object sender, PointerReleasedEventArgs e)
+ {
+ // Arrête le suivi de la souris
+ _isDragging = false;
+
+ var parent = _ParentWM;
+ var currentPosition = e.GetPosition( _ParentWM);
+ if (parent != null)
+ {
+
+ // Vérifier la largeur et la hauteur du parent
+ var parentWidth = parent.Bounds.Width;
+ var parentHeight = parent.Bounds.Height;
+
+ // Si la position X est à moins de 50 pixels du bord gauche
+ if (currentPosition.X < 60)
+ {
+ // Redimensionner et repositionner la fenêtre pour occuper toute la moitié gauche du parent
+ _windowBorder.Margin = new Thickness(0, 0, parentWidth / 2, 0);
+ _windowBorder.Animate(WidthProperty, _windowBorder.Width,parentWidth / 2);
+ _windowBorder.Animate(HeightProperty, _windowBorder.Height,parentHeight);
+
+ }
+ // Si la position X est à moins de 50 pixels du bord droit
+ else if (currentPosition.X + _windowBorder.Width > parentWidth - 60)
+ {
+ // Redimensionner et repositionner la fenêtre pour occuper toute la moitié droite du parent
+ _windowBorder.Margin = new Thickness(parentWidth / 2, 0, 0, 0);
+
+ _windowBorder.Animate(WidthProperty, _windowBorder.Width,parentWidth / 2);
+ _windowBorder.Animate(HeightProperty, _windowBorder.Height,parentHeight);
+ }
+ // Si la position Y est à moins de 50 pixels du bord supérieur
+ else if (currentPosition.Y < 60)
+ {
+ // Redimensionner et repositionner la fenêtre pour occuper tout l'écran
+ _windowBorder.Margin = new Thickness(0);
+
+ _windowBorder.Animate(WidthProperty, _windowBorder.Width,parentWidth );
+ _windowBorder.Animate(HeightProperty, _windowBorder.Height,parentHeight);
+ }
+ }
+ }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public event EventHandler Closed;
+
+ private void CloseButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ Closed?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void resizeborder(object? sender, PointerPressedEventArgs e)
+ {
+ _startPoint = e.GetPosition(_windowBorder);
+ _isResizing= true;
+ _initialSize = new Size(_windowBorder.Width, _windowBorder.Height);
+ }
+
+ private void rezisebordermove(object? sender, PointerEventArgs e)
+ {
+ if (_isResizing)
+ {
+ // Obtenir la position actuelle de la souris
+ var currentPosition = e.GetPosition(_windowBorder);
+ var deltaX = currentPosition.X - _startPoint.X;
+ var deltaY = currentPosition.Y - _startPoint.Y;
+
+ // Calculer la nouvelle taille
+ var newWidth = Math.Max(_initialSize.Width + deltaX, 100); // Limite minimale de taille
+ var newHeight = Math.Max(_initialSize.Height , 100);
+
+ // Appliquer la nouvelle taille
+ _windowBorder.Width = newWidth;
+ _windowBorder.Height = newHeight;
+ }
+ }
+
+ private void rezisebordermoveNS(object? sender, PointerEventArgs e)
+ {
+
+ if (_isResizing)
+ {
+ // Obtenir la position actuelle de la souris
+ var currentPosition = e.GetPosition(_windowBorder);
+ var deltaX = currentPosition.X - _startPoint.X;
+ var deltaY = currentPosition.Y - _startPoint.Y;
+
+ // Calculer la nouvelle taille
+ var newWidth = Math.Max(_initialSize.Width , 100); // Limite minimale de taille
+ var newHeight = Math.Max(_initialSize.Height + deltaY, 100);
+
+ // Appliquer la nouvelle taille
+ _windowBorder.Width = newWidth;
+ _windowBorder.Height = newHeight;
+ }
+ }
+
+ private void resizereleased(object? sender, PointerReleasedEventArgs e)
+ {
+ _isResizing = false;
+ }
+
+ public void ChangeVisibility()
+ {
+ if (isVisible)
+ _windowBorder.Animate(OpacityProperty,1,0);
+ else
+ _windowBorder.Animate(OpacityProperty,0,1);
+
+ _windowBorder.IsHitTestVisible = !isVisible;
+
+ isVisible = !isVisible;
+ }
+
+ private void PART_MaximizeButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ // Redimensionner et repositionner la fenêtre pour occuper tout l'écran
+ _windowBorder.Margin = new Thickness(0);
+
+ _windowBorder.Animate(WidthProperty, _windowBorder.Width,_ParentWM.Bounds.Width );
+ _windowBorder.Animate(HeightProperty, _windowBorder.Height,_ParentWM.Bounds.Height);
+ }
+
+ private void PART_MinimizeButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ ChangeVisibility();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/SDESoftware.cs b/SukiUI/Controls/Experimental/DesktopEnvironment/SDESoftware.cs
new file mode 100644
index 000000000..1b4212353
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/SDESoftware.cs
@@ -0,0 +1,41 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using SukiUI.Helpers;
+
+namespace SukiUI.Controls.Experimental.DesktopEnvironment
+{
+ public class SDESoftware : SukiObservableObject
+ {
+ public IImage? Icon { get; set; }
+
+ public string Name { get; set; }
+
+ public Type Content { get; set; }
+
+
+
+ private InternalWindow instance = null;
+
+ public InternalWindow Instance
+ {
+ get => instance;
+ set
+ {
+ SetAndRaise(ref instance, value);
+ }
+ }
+
+ public void Click(WindowManager wm)
+ {
+ if (Instance == null)
+ {
+ Instance = new InternalWindow(wm, (Control)Activator.CreateInstance(Content), Name);
+ Instance.Closed += (sender, args) => Instance = null;
+ wm.OpenWindow(Instance);
+ return;
+ }
+
+ Instance.ChangeVisibility();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/SukiDesktopEnvironment.axaml b/SukiUI/Controls/Experimental/DesktopEnvironment/SukiDesktopEnvironment.axaml
new file mode 100644
index 000000000..12fb3b011
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/SukiDesktopEnvironment.axaml
@@ -0,0 +1,99 @@
+
+
+
+
+
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/SukiDesktopEnvironment.axaml.cs b/SukiUI/Controls/Experimental/DesktopEnvironment/SukiDesktopEnvironment.axaml.cs
new file mode 100644
index 000000000..316e83eb1
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/SukiDesktopEnvironment.axaml.cs
@@ -0,0 +1,69 @@
+using System.Collections.ObjectModel;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+
+namespace SukiUI.Controls.Experimental.DesktopEnvironment
+{
+ public partial class SukiDesktopEnvironment : UserControl
+ {
+ private WindowManager _WM;
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _WM = e.NameScope.Find("WM");
+ }
+ public SukiDesktopEnvironment()
+ {
+ InitializeComponent();
+ }
+
+ public static readonly StyledProperty DesktopBackgroundImageSourceProperty =
+ AvaloniaProperty.Register(nameof(DesktopBackgroundImageSource));
+
+ public IImage? DesktopBackgroundImageSource
+ {
+ get => GetValue(DesktopBackgroundImageSourceProperty);
+ set => SetValue(DesktopBackgroundImageSourceProperty, value);
+ }
+
+ public static readonly StyledProperty HomeImageSourceProperty =
+ AvaloniaProperty.Register(nameof(HomeImageSource));
+
+ public IImage? HomeImageSource
+ {
+ get => GetValue(HomeImageSourceProperty);
+ set => SetValue(HomeImageSourceProperty, value);
+ }
+
+ private void open(object? sender, RoutedEventArgs e)
+ {
+ SDESoftware soft = (SDESoftware)(((Button)sender).Tag);
+ soft.Click(_WM);
+ }
+
+ public static readonly StyledProperty> SoftwaresProperty =
+ AvaloniaProperty.Register>(nameof(Softwares),
+ defaultValue: new ObservableCollection());
+
+ public ObservableCollection Softwares
+ {
+ get { return GetValue(SoftwaresProperty); }
+ set { SetValue(SoftwaresProperty, value); }
+ }
+
+ private void InputElement_OnPointerPressed(object sender, PointerPressedEventArgs e)
+ {
+ var ctl = sender as Control;
+ if (ctl != null)
+ {
+ FlyoutBase.ShowAttachedFlyout(ctl);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/WindowManager.axaml b/SukiUI/Controls/Experimental/DesktopEnvironment/WindowManager.axaml
new file mode 100644
index 000000000..29d52c410
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/WindowManager.axaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/SukiUI/Controls/Experimental/DesktopEnvironment/WindowManager.axaml.cs b/SukiUI/Controls/Experimental/DesktopEnvironment/WindowManager.axaml.cs
new file mode 100644
index 000000000..0bbf98f8d
--- /dev/null
+++ b/SukiUI/Controls/Experimental/DesktopEnvironment/WindowManager.axaml.cs
@@ -0,0 +1,30 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace SukiUI.Controls.Experimental.DesktopEnvironment
+{
+ public partial class WindowManager : UserControl
+ {
+ private Canvas _canvas;
+
+ public WindowManager()
+ {
+ _canvas = new Canvas();
+ this.Content = _canvas;
+ }
+
+ public void OpenWindow(InternalWindow window)
+ {
+
+
+ window.Closed += (s, e) => _canvas.Children.Remove(window);
+
+ _canvas.Children.Add(window);
+
+ // Positionner la fenêtre
+ Canvas.SetLeft(window, 0);
+ Canvas.SetTop(window, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SukiUI/SukiUI.csproj b/SukiUI/SukiUI.csproj
index 9c49c1f67..0db9eee66 100644
--- a/SukiUI/SukiUI.csproj
+++ b/SukiUI/SukiUI.csproj
@@ -83,6 +83,10 @@
SukiDialog.axaml
+
+ ChatUI.axaml
+ Code
+