├── .gitignore
├── PSD
├── Dark2.psd
├── Flat.psd
├── Metal2.psd
└── FlatLight.psd
├── screenshot-0.2.0.jpg
├── CleverDock
├── Content
│ ├── unknown.png
│ └── Effects
│ │ ├── BrightnessEffect.ps
│ │ ├── BrightnessEffect.fx
│ │ └── BrightnessEffect.cs
├── Themes
│ ├── Dark2
│ │ ├── bg.png
│ │ ├── theme.json
│ │ └── style.xaml
│ ├── Flat
│ │ ├── bg.png
│ │ ├── theme.json
│ │ └── style.xaml
│ ├── Metal2
│ │ ├── bg.png
│ │ └── style.xaml
│ └── FlatLight
│ │ ├── bg.png
│ │ ├── theme.json
│ │ └── style.xaml
├── App.config
├── Views
│ ├── MainWindow.xaml.cs
│ ├── WidgetsWindow.xaml
│ ├── DraggedIconWindow.xaml
│ ├── Converters
│ │ └── AdditionConverter.cs
│ ├── DraggedIconWindow.xaml.cs
│ ├── WidgetsWindow.xaml.cs
│ └── MainWindow.xaml
├── Handlers
│ ├── WindowRectEventArgs.cs
│ ├── ThemeEventArgs.cs
│ ├── WindowEventArgs.cs
│ ├── MouseMoveEventArgs.cs
│ └── MouseButtonEventArgs.cs
├── Behaviors
│ ├── DockIconLoadedBehavior.cs
│ ├── DockIconBounceBehavior.cs
│ ├── DockAutoHideBehavior.cs
│ └── DockIconDragBehavior.cs
├── App.xaml.cs
├── AssemblyInfo.cs
├── Tools
│ ├── PathTools.cs
│ ├── XamlHelper.cs
│ ├── FrameworkHelper.cs
│ ├── StringUtils.cs
│ ├── BitmapEffectHelper.cs
│ └── AnimationTools.cs
├── Models
│ ├── ThemeModel.cs
│ ├── IconModel.cs
│ ├── Patterns
│ │ ├── VMLocator.cs
│ │ ├── SerializableModelBase.cs
│ │ ├── ViewModelBase.cs
│ │ └── ActionCommand.cs
│ ├── ViewModels
│ │ ├── ThemeViewModel.cs
│ │ ├── ThemeSettingsViewModel.cs
│ │ ├── MainViewModel.cs
│ │ └── IconViewModel.cs
│ ├── ThemeSettingsModel.cs
│ └── SettingsModel.cs
├── Managers
│ ├── WorkAreaManager.cs
│ ├── ProcessManager.cs
│ ├── TaskbarManager.cs
│ ├── MouseManager.cs
│ ├── ThemeManager.cs
│ ├── IconManager.cs
│ ├── DockManager.cs
│ └── WindowManager.cs
├── Helpers
│ └── ScreenHelper.cs
├── App.xaml
├── Interop
│ ├── DwmInterop.cs
│ ├── Win32Window.cs
│ ├── ProcessInterop.cs
│ └── IconInterop.cs
└── CleverDock.csproj
├── config.json
├── LICENSE.txt
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | obj
2 | bin
3 | .idea
4 | packages
5 |
--------------------------------------------------------------------------------
/PSD/Dark2.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/PSD/Dark2.psd
--------------------------------------------------------------------------------
/PSD/Flat.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/PSD/Flat.psd
--------------------------------------------------------------------------------
/PSD/Metal2.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/PSD/Metal2.psd
--------------------------------------------------------------------------------
/PSD/FlatLight.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/PSD/FlatLight.psd
--------------------------------------------------------------------------------
/screenshot-0.2.0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/screenshot-0.2.0.jpg
--------------------------------------------------------------------------------
/CleverDock/Content/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/CleverDock/Content/unknown.png
--------------------------------------------------------------------------------
/CleverDock/Themes/Dark2/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/CleverDock/Themes/Dark2/bg.png
--------------------------------------------------------------------------------
/CleverDock/Themes/Flat/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/CleverDock/Themes/Flat/bg.png
--------------------------------------------------------------------------------
/CleverDock/Themes/Metal2/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/CleverDock/Themes/Metal2/bg.png
--------------------------------------------------------------------------------
/CleverDock/Themes/FlatLight/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/CleverDock/Themes/FlatLight/bg.png
--------------------------------------------------------------------------------
/CleverDock/Content/Effects/BrightnessEffect.ps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dlacaille/clever-dock/HEAD/CleverDock/Content/Effects/BrightnessEffect.ps
--------------------------------------------------------------------------------
/CleverDock/Themes/Flat/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "themeName": "Flat",
3 | "author": "Dominic Lacaille",
4 | "version": "1.0.0",
5 | "description": "",
6 | "iconPaddingX": 15,
7 | "iconPaddingY": 20
8 | }
--------------------------------------------------------------------------------
/CleverDock/Themes/Dark2/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "themeName": "Metal Dark",
3 | "author": "Dominic Lacaille",
4 | "version": "2.0.0",
5 | "description": "",
6 | "iconPaddingX": 15,
7 | "iconPaddingY": 20
8 | }
--------------------------------------------------------------------------------
/CleverDock/Themes/FlatLight/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "themeName": "Flat Light",
3 | "author": "Dominic Lacaille",
4 | "version": "1.0.0",
5 | "description": "",
6 | "iconPaddingX": 15,
7 | "iconPaddingY": 20
8 | }
--------------------------------------------------------------------------------
/CleverDock/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CleverDock/Views/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace CleverDock;
4 |
5 | ///
6 | /// Interaction logic for MainWindow.xaml
7 | ///
8 | public partial class MainWindow : Window
9 | {
10 | }
--------------------------------------------------------------------------------
/CleverDock/Handlers/WindowRectEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace CleverDock.Handlers;
4 |
5 | public class WindowRectEventArgs : EventArgs
6 | {
7 | public WindowRectEventArgs(Rect rect)
8 | {
9 | Rect = rect;
10 | }
11 |
12 | public Rect Rect { get; set; }
13 | }
--------------------------------------------------------------------------------
/CleverDock/Handlers/ThemeEventArgs.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Models;
2 |
3 | namespace CleverDock.Handlers;
4 |
5 | public class ThemeEventArgs : EventArgs
6 | {
7 | public ThemeEventArgs(ThemeModel theme)
8 | {
9 | Theme = theme;
10 | }
11 |
12 | public ThemeModel Theme { get; set; }
13 | }
--------------------------------------------------------------------------------
/CleverDock/Handlers/WindowEventArgs.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Interop;
2 |
3 | namespace CleverDock.Handlers;
4 |
5 | public class WindowEventArgs : EventArgs
6 | {
7 | public WindowEventArgs(Win32Window window)
8 | {
9 | Window = window;
10 | }
11 |
12 | public Win32Window Window { get; set; }
13 | }
--------------------------------------------------------------------------------
/CleverDock/Handlers/MouseMoveEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace CleverDock.Handlers;
4 |
5 | public class MouseMoveEventArgs : EventArgs
6 | {
7 | public MouseMoveEventArgs(Point cursorPosition)
8 | {
9 | CursorPosition = cursorPosition;
10 | }
11 |
12 | public Point CursorPosition { get; set; }
13 | }
--------------------------------------------------------------------------------
/CleverDock/Content/Effects/BrightnessEffect.fx:
--------------------------------------------------------------------------------
1 | sampler2D input : register(S0);
2 | float brightness : register(C0);
3 | float contrast : register(C1);
4 |
5 | float4 main(float2 uv : TEXCOORD) : COLOR
6 | {
7 | float4 color = tex2D(input, uv);
8 | float4 result = float4(color.rgb + brightness, color.a);
9 | result = result * (1.0+contrast)/1.0;
10 | result.rgb *= color.a; // Premultiplied alpha magic
11 | return result;
12 | }
--------------------------------------------------------------------------------
/CleverDock/Behaviors/DockIconLoadedBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using CleverDock.ViewModels;
3 | using Microsoft.Xaml.Behaviors;
4 |
5 | namespace CleverDock.Behaviors;
6 |
7 | public class DockIconLoadedBehavior : Behavior
8 | {
9 | protected override void OnAttached()
10 | {
11 | base.OnAttached();
12 | var icon = (IconViewModel)AssociatedObject.DataContext;
13 | icon.Element = AssociatedObject;
14 | }
15 | }
--------------------------------------------------------------------------------
/CleverDock/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Media.Animation;
3 |
4 | namespace CleverDock;
5 |
6 | ///
7 | /// Interaction logic for App.xaml
8 | ///
9 | public partial class App : Application
10 | {
11 | public App()
12 | {
13 | // Change Timeline frame rate to 60fps.
14 | Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline),
15 | new FrameworkPropertyMetadata { DefaultValue = 60 });
16 | }
17 | }
--------------------------------------------------------------------------------
/CleverDock/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
--------------------------------------------------------------------------------
/CleverDock/Tools/PathTools.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace CleverDock.Tools;
4 |
5 | public class PathTools
6 | {
7 | public static bool SamePath(string a, string b)
8 | {
9 | if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b))
10 | return a == b;
11 | return string.Compare(
12 | Path.GetFullPath(a).TrimEnd('\\'),
13 | Path.GetFullPath(b).TrimEnd('\\'),
14 | StringComparison.InvariantCultureIgnoreCase) == 0;
15 | }
16 | }
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "0.4.1",
3 | "CollapseDuration": 0.2,
4 | "DockHideDuration": 0.5,
5 | "DockShowDuration": 0.3,
6 | "DockHideDelay": 0.0,
7 | "DockShowDelay": 0.0,
8 | "HotspotHeight": 20,
9 | "DockEdgeSpacing": 20,
10 | "IconSize": 48,
11 | "Theme": null,
12 | "Icons": [],
13 | "SaveAutomatically": true,
14 | "ReserveScreenSpace": false,
15 | "RemoveTaskbar": true,
16 | "AutoHide": true,
17 | "ShowWidgets": true,
18 | "OuterIconWidth": 0,
19 | "OuterIconHeight": 0
20 | }
--------------------------------------------------------------------------------
/CleverDock/Tools/XamlHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Windows;
3 | using System.Windows.Markup;
4 |
5 | namespace CleverDock.Tools;
6 |
7 | public class XamlHelper
8 | {
9 | public static ResourceDictionary LoadXaml(string path)
10 | {
11 | ResourceDictionary resourceDict;
12 | using (var fs = new FileStream(path, FileMode.Open))
13 | {
14 | resourceDict = (ResourceDictionary)XamlReader.Load(fs);
15 | }
16 |
17 | return resourceDict;
18 | }
19 | }
--------------------------------------------------------------------------------
/CleverDock/Handlers/MouseButtonEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace CleverDock.Handlers;
4 |
5 | public enum MouseButton
6 | {
7 | Left,
8 | Right
9 | }
10 |
11 | public class MouseButtonEventArgs : EventArgs
12 | {
13 | public MouseButtonEventArgs(MouseButton mouseButton, Point cursorPosition)
14 | {
15 | CursorPosition = cursorPosition;
16 | MouseButton = mouseButton;
17 | }
18 |
19 | public Point CursorPosition { get; set; }
20 | public MouseButton MouseButton { get; set; }
21 | }
--------------------------------------------------------------------------------
/CleverDock/Views/WidgetsWindow.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/CleverDock/Views/DraggedIconWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CleverDock/Views/Converters/AdditionConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Windows.Data;
3 |
4 | namespace CleverDock.Converters;
5 |
6 | public class AdditionConverter : IValueConverter
7 | {
8 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
9 | {
10 | var number = double.Parse(parameter.ToString());
11 | return double.Parse(value.ToString()) + number;
12 | }
13 |
14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | var number = double.Parse(parameter.ToString());
17 | return double.Parse(value.ToString()) - number;
18 | }
19 | }
--------------------------------------------------------------------------------
/CleverDock/Models/ThemeModel.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace CleverDock.Models;
4 |
5 | public class ThemeModel
6 | {
7 | public ThemeModel()
8 | {
9 | }
10 |
11 | public ThemeModel(string path)
12 | {
13 | var fileInfo = new FileInfo(path);
14 | var settings = new ThemeSettingsModel(fileInfo.Directory.FullName);
15 | Name = settings.ThemeName;
16 | Path = fileInfo.Directory.FullName;
17 | }
18 |
19 | ///
20 | /// Name of the theme.
21 | ///
22 | public string Name { get; set; }
23 |
24 | ///
25 | /// Path of the theme.
26 | ///
27 | public string Path { get; set; }
28 | }
--------------------------------------------------------------------------------
/CleverDock/Models/IconModel.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Patterns;
2 |
3 | namespace CleverDock.Models;
4 |
5 | ///
6 | /// Stores the information for a dock icon.
7 | ///
8 | public class IconModel : SerializableModelBase
9 | {
10 | ///
11 | /// The path to the icon's executable or file.
12 | ///
13 | public string Path { get; set; }
14 |
15 | ///
16 | /// Optional. The path to the image used for the icon, if set to null the system icon will be used.
17 | ///
18 | public string ImagePath { get; set; }
19 |
20 | ///
21 | /// The name of the icon
22 | ///
23 | public string Name { get; set; }
24 | }
--------------------------------------------------------------------------------
/CleverDock/Models/Patterns/VMLocator.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.ViewModels;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace CleverDock.Patterns;
5 |
6 | public class VMLocator
7 | {
8 | private static readonly IServiceProvider provider;
9 |
10 | static VMLocator()
11 | {
12 | var container = new ServiceCollection();
13 | container.AddSingleton();
14 | container.AddSingleton();
15 | provider = container.BuildServiceProvider();
16 | }
17 |
18 | public static MainViewModel Main => provider.GetRequiredService();
19 |
20 | public static ThemeSettingsViewModel ThemeSettings => provider.GetRequiredService();
21 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/WorkAreaManager.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Interop;
2 |
3 | namespace CleverDock.Managers;
4 |
5 | internal class WorkAreaManager
6 | {
7 | public static void SetWorkingArea(int left, int top, int right, int bottom)
8 | {
9 | var rect = new SystemInterop.RECT { Left = left, Top = top, Right = right, Bottom = bottom };
10 | SystemInterop.SystemParametersInfo(SystemInterop.SPI.SPI_SETWORKAREA, 0, ref rect,
11 | SystemInterop.SPIF.SPIF_UPDATEINIFILE);
12 | }
13 |
14 | public static SystemInterop.RECT GetWorkingArea()
15 | {
16 | var rect = new SystemInterop.RECT();
17 | SystemInterop.SystemParametersInfo(SystemInterop.SPI.SPI_GETWORKAREA, 0, ref rect,
18 | SystemInterop.SPIF.SPIF_UPDATEINIFILE);
19 | return rect;
20 | }
21 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/ProcessManager.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using CleverDock.Interop;
3 |
4 | namespace CleverDock.Managers;
5 |
6 | public class ProcessManager
7 | {
8 | // Vista +
9 | public static string GetExecutablePath(int dwProcessId)
10 | {
11 | var buffer = new StringBuilder(1024);
12 | var hprocess = ProcessInterop.OpenProcess(ProcessInterop.PROCESS_QUERY_LIMITED_INFORMATION, false, dwProcessId);
13 | if (hprocess == IntPtr.Zero)
14 | return string.Empty;
15 | try
16 | {
17 | var size = buffer.Capacity;
18 | if (ProcessInterop.QueryFullProcessImageName(hprocess, 0, buffer, out size))
19 | return buffer.ToString();
20 | }
21 | finally
22 | {
23 | ProcessInterop.CloseHandle(hprocess);
24 | }
25 |
26 | return string.Empty;
27 | }
28 | }
--------------------------------------------------------------------------------
/CleverDock/Helpers/ScreenHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace CleverDock.Helpers;
4 |
5 | public static class ScreenHelper
6 | {
7 | public static Size GetScreenResolution(bool transformToDevice = false)
8 | {
9 | if (transformToDevice)
10 | {
11 | if (Application.Current.MainWindow == null)
12 | throw new Exception("Cannot find the main window");
13 | var ps = PresentationSource.FromVisual(Application.Current.MainWindow);
14 | if (ps?.CompositionTarget == null)
15 | throw new Exception("Cannot find a valid presentation source");
16 | var m = ps.CompositionTarget.TransformToDevice;
17 | return new Size(
18 | SystemParameters.PrimaryScreenWidth * m.M22,
19 | SystemParameters.PrimaryScreenHeight * m.M11
20 | );
21 | }
22 |
23 | return new Size(
24 | SystemParameters.PrimaryScreenWidth,
25 | SystemParameters.PrimaryScreenHeight
26 | );
27 | }
28 | }
--------------------------------------------------------------------------------
/CleverDock/Tools/FrameworkHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Media;
3 |
4 | namespace CleverDock.Tools;
5 |
6 | public class FrameworkHelper
7 | {
8 | public static T GetParent(Visual v)
9 | where T : Visual
10 | {
11 | while (v != null)
12 | {
13 | v = VisualTreeHelper.GetParent(v) as Visual;
14 | if (v is T)
15 | break;
16 | }
17 |
18 | return v as T;
19 | }
20 |
21 | public static T GetVisualChild(DependencyObject parent)
22 | where T : Visual
23 | {
24 | var child = default(T);
25 | var numVisuals = VisualTreeHelper.GetChildrenCount(parent);
26 | for (var i = 0; i < numVisuals; i++)
27 | {
28 | var v = (Visual)VisualTreeHelper.GetChild(parent, i);
29 | child = v as T;
30 | if (child == null)
31 | child = GetVisualChild(v);
32 | if (child != null)
33 | break;
34 | }
35 |
36 | return child;
37 | }
38 | }
--------------------------------------------------------------------------------
/CleverDock/Models/Patterns/SerializableModelBase.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Newtonsoft.Json;
3 |
4 | namespace CleverDock.Patterns;
5 |
6 | public class SerializableModelBase
7 | {
8 | public void LoadFromFile(string path)
9 | {
10 | if (!File.Exists(path))
11 | return;
12 | using (var stream = new StreamReader(path))
13 | {
14 | try
15 | {
16 | JsonConvert.PopulateObject(stream.ReadToEnd(), this);
17 | }
18 | catch (InvalidOperationException ex)
19 | {
20 | Console.WriteLine(ex.Message);
21 | }
22 | }
23 | }
24 |
25 | public void SaveAsFile(string path)
26 | {
27 | var json = JsonConvert.SerializeObject(this, Formatting.Indented);
28 | using (var stream = new StreamWriter(path, false))
29 | {
30 | stream.Write(json);
31 | }
32 | }
33 |
34 | public bool CanDeserialize(string path)
35 | {
36 | if (!File.Exists(path))
37 | return false;
38 | return true; // TODO: Check if json is valid.
39 | }
40 | }
--------------------------------------------------------------------------------
/CleverDock/Tools/StringUtils.cs:
--------------------------------------------------------------------------------
1 | namespace CleverDock.Tools;
2 |
3 | public class StringUtils
4 | {
5 | ///
6 | /// Limits characters according to the lower limit but keeps the string whole if it is shorter than the upper limit.
7 | /// Tries to cut at words if spaces are found within the lower and upper limit.
8 | ///
9 | /// Text to limit.
10 | ///
11 | /// Lower limit to start cutting words./param>
12 | /// Maximum string size.
13 | ///
14 | public static string LimitCharacters(string input, int lowerLimit, int upperLimit)
15 | {
16 | if (input.Length < upperLimit)
17 | return input;
18 | var margin = input.Substring(lowerLimit, upperLimit - lowerLimit);
19 | if (margin.Contains(' '))
20 | {
21 | var lastSpace = margin.LastIndexOf(' ');
22 | return input.Substring(0, lastSpace + lowerLimit) + "...";
23 | }
24 |
25 | return input.Substring(0, lowerLimit) + "...";
26 | }
27 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Dominic Lacaille
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CleverDock/Views/DraggedIconWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Interop;
3 | using CleverDock.Interop;
4 | using CleverDock.Managers;
5 |
6 | namespace CleverDock;
7 |
8 | ///
9 | /// Interaction logic for Window1.xaml
10 | ///
11 | public partial class DraggedIconWindow : Window
12 | {
13 | public DraggedIconWindow()
14 | {
15 | InitializeComponent();
16 | ShowInTaskbar = false;
17 | Loaded += DraggedIconWindow_Loaded;
18 | WindowManager.Manager.ActiveWindowChanged += Manager_ActiveWindowChanged;
19 | }
20 |
21 | private void DraggedIconWindow_Loaded(object sender, RoutedEventArgs e)
22 | {
23 | SetTopmost();
24 | }
25 |
26 | private void Manager_ActiveWindowChanged(object sender, EventArgs e)
27 | {
28 | SetTopmost();
29 | }
30 |
31 | public void SetTopmost()
32 | {
33 | Application.Current.Dispatcher.Invoke(() =>
34 | {
35 | var hwnd = new WindowInteropHelper(this).Handle;
36 | WindowInterop.SetWindowPos(hwnd, WindowInterop.HWND_TOPMOST, 0, 0, 0, 0,
37 | WindowInterop.SWP_NOMOVE | WindowInterop.SWP_NOSIZE);
38 | });
39 | }
40 | }
--------------------------------------------------------------------------------
/CleverDock/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/CleverDock/Models/ViewModels/ThemeViewModel.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Managers;
2 | using CleverDock.Patterns;
3 |
4 | namespace CleverDock.Models.ViewModels;
5 |
6 | public class ThemeViewModel : ViewModelBase
7 | {
8 | private readonly ThemeModel model = new();
9 |
10 | public ThemeViewModel(ThemeModel _model)
11 | {
12 | model = _model;
13 | LoadCommand = new ActionCommand(LoadAction);
14 | }
15 |
16 | ///
17 | /// Name of the theme.
18 | ///
19 | public string Name
20 | {
21 | get => model.Name;
22 | set
23 | {
24 | if (value != model.Name)
25 | {
26 | model.Name = value;
27 | OnPropertyChanged();
28 | }
29 | }
30 | }
31 |
32 | ///
33 | /// Path of the theme.
34 | ///
35 | public string Path
36 | {
37 | get => model.Path;
38 | set
39 | {
40 | if (value != model.Path)
41 | {
42 | model.Path = value;
43 | OnPropertyChanged();
44 | }
45 | }
46 | }
47 |
48 | public ActionCommand LoadCommand { get; private set; }
49 |
50 | private void LoadAction()
51 | {
52 | ThemeManager.Manager.LoadTheme(model);
53 | }
54 | }
--------------------------------------------------------------------------------
/CleverDock/Models/Patterns/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Runtime.CompilerServices;
3 | using System.Windows;
4 |
5 | namespace CleverDock.Patterns;
6 |
7 | public class ViewModelBase : INotifyPropertyChanged
8 | {
9 | private bool? _isInDesignMode;
10 |
11 | ///
12 | /// Gets a value indicating whether the control is in design mode.
13 | ///
14 | protected bool IsInDesignMode
15 | {
16 | get
17 | {
18 | if (!_isInDesignMode.HasValue)
19 | {
20 | var prop = DesignerProperties.IsInDesignModeProperty;
21 | _isInDesignMode = (bool)DependencyPropertyDescriptor
22 | .FromProperty(prop, typeof(FrameworkElement))
23 | .Metadata.DefaultValue;
24 | }
25 |
26 | return _isInDesignMode.Value;
27 | }
28 | }
29 |
30 | public event PropertyChangedEventHandler PropertyChanged;
31 |
32 | protected void OnPropertyChanged(PropertyChangedEventArgs e)
33 | {
34 | var handler = PropertyChanged;
35 | if (handler != null)
36 | handler(this, e);
37 | }
38 |
39 | protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
40 | {
41 | OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
42 | }
43 | }
--------------------------------------------------------------------------------
/CleverDock/Behaviors/DockIconBounceBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Media;
3 | using System.Windows.Media.Animation;
4 | using CleverDock.ViewModels;
5 | using Microsoft.Xaml.Behaviors;
6 |
7 | namespace CleverDock.Behaviors;
8 |
9 | public class DockIconBounceBehavior : Behavior
10 | {
11 | private IconViewModel icon;
12 |
13 | protected override void OnAttached()
14 | {
15 | base.OnAttached();
16 | icon = (IconViewModel)AssociatedObject.DataContext;
17 | icon.OnAnimateIconBounce += icon_OnAnimateIconBounce;
18 | }
19 |
20 | private void icon_OnAnimateIconBounce(object sender, EventArgs e)
21 | {
22 | BounceIcon();
23 | }
24 |
25 | public void BounceIcon()
26 | {
27 | var translation = new DoubleAnimation
28 | {
29 | From = 0,
30 | To = -30,
31 | Duration = TimeSpan.FromSeconds(1.2),
32 | AutoReverse = true
33 | };
34 | translation.EasingFunction = new BounceEase
35 | {
36 | Bounces = 1,
37 | Bounciness = 1,
38 | EasingMode = EasingMode.EaseIn
39 | };
40 | Storyboard.SetTargetName(translation, "ImageTransform");
41 | Storyboard.SetTargetProperty(translation, new PropertyPath(TranslateTransform.YProperty));
42 |
43 | var sb = new Storyboard();
44 | sb.Children.Add(translation);
45 | sb.Begin(AssociatedObject);
46 | }
47 | }
--------------------------------------------------------------------------------
/CleverDock/Models/ThemeSettingsModel.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using CleverDock.Patterns;
3 | using Newtonsoft.Json;
4 |
5 | namespace CleverDock.Models;
6 |
7 | public class ThemeSettingsModel : SerializableModelBase
8 | {
9 | public ThemeSettingsModel()
10 | {
11 | IconPaddingX = 15;
12 | IconPaddingY = 20;
13 | }
14 |
15 | public ThemeSettingsModel(string path)
16 | {
17 | var filename = Path.Combine(path, "theme.json");
18 | if (!File.Exists(filename))
19 | return;
20 | using (var stream = new StreamReader(filename))
21 | {
22 | try
23 | {
24 | JsonConvert.PopulateObject(stream.ReadToEnd(), this);
25 | }
26 | catch (InvalidOperationException ex)
27 | {
28 | Console.WriteLine(ex.Message);
29 | }
30 | }
31 | }
32 |
33 | ///
34 | /// Name of the Theme.
35 | ///
36 | public string ThemeName { get; set; }
37 |
38 | ///
39 | /// Author of the Theme.
40 | ///
41 | public string Author { get; set; }
42 |
43 | ///
44 | /// Version of the Theme.
45 | ///
46 | public string Version { get; set; }
47 |
48 | ///
49 | /// Description of the Theme.
50 | ///
51 | public string Description { get; set; }
52 |
53 | ///
54 | /// X icon padding.
55 | ///
56 | public int IconPaddingX { get; set; }
57 |
58 | ///
59 | /// Y icon padding
60 | ///
61 | public int IconPaddingY { get; set; }
62 | }
--------------------------------------------------------------------------------
/CleverDock/Tools/BitmapEffectHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Windows.Media.Imaging;
3 | using Kaliko.ImageLibrary;
4 | using Kaliko.ImageLibrary.Filters;
5 |
6 | namespace CleverDock.Tools;
7 |
8 | public class BitmapEffectHelper
9 | {
10 | public static BitmapImage GaussianBlur(BitmapSource image, float radius)
11 | {
12 | var filter = new GaussianBlurFilter(radius);
13 | using (var kimage = BitmapSourceToKaliko(image))
14 | {
15 | if (kimage == null)
16 | return null;
17 | var padding = (int)Math.Ceiling(radius);
18 | kimage.Crop(-padding, padding, (int)image.Width + padding * 2, (int)image.Height + padding * 2);
19 | kimage.ApplyFilter(filter);
20 | return KalikoToBitmap(kimage);
21 | }
22 | }
23 |
24 | private static KalikoImage BitmapSourceToKaliko(BitmapSource image)
25 | {
26 | if (image == null)
27 | return null;
28 | var stream = new MemoryStream();
29 | BitmapEncoder enc = new PngBitmapEncoder();
30 | enc.Frames.Add(BitmapFrame.Create(image));
31 | enc.Save(stream);
32 | stream.Position = 0;
33 | return new KalikoImage(stream);
34 | }
35 |
36 | private static BitmapImage KalikoToBitmap(KalikoImage kimage)
37 | {
38 | var stream = new MemoryStream();
39 | kimage.SavePng(stream);
40 | stream.Position = 0;
41 | var bitmap = new BitmapImage();
42 | bitmap.BeginInit();
43 | bitmap.CacheOption = BitmapCacheOption.OnLoad;
44 | bitmap.StreamSource = stream;
45 | bitmap.EndInit();
46 | bitmap.Freeze();
47 | return bitmap;
48 | }
49 | }
--------------------------------------------------------------------------------
/CleverDock/Interop/DwmInterop.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace CleverDock.Interop;
4 |
5 | internal class DwmInterop
6 | {
7 | public const int DWM_TNP_RECTDESTINATION = 0x00000001;
8 | public const int DWM_TNP_RECTSOURCE = 0x00000002;
9 | public const int DWM_TNP_OPACITY = 0x00000004;
10 | public const int DWM_TNP_VISIBLE = 0x00000008;
11 | public const int DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010;
12 |
13 | ///
14 | /// Creates a Desktop Window Manager (DWM) thumbnail relationship between the destination and source windows.
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | [DllImport("dwmapi.dll", SetLastError = true)]
21 | public static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb);
22 |
23 | ///
24 | /// Updates the properties for a Desktop Window Manager (DWM) thumbnail.
25 | ///
26 | ///
27 | ///
28 | ///
29 | [DllImport("dwmapi.dll", PreserveSig = true)]
30 | public static extern int DwmUpdateThumbnailProperties(IntPtr hThumbnail, ref DWM_THUMBNAIL_PROPERTIES props);
31 |
32 | [StructLayout(LayoutKind.Sequential)]
33 | public struct DWM_THUMBNAIL_PROPERTIES
34 | {
35 | public long dwFlags;
36 | public RECT rcDestination;
37 | public RECT rcSource;
38 | public byte opacity;
39 | public bool fVisible;
40 | public bool fSourceClientAreaOnly;
41 | }
42 |
43 | [StructLayout(LayoutKind.Sequential)]
44 | public struct RECT
45 | {
46 | public long left, top, right, bottom;
47 | }
48 | }
--------------------------------------------------------------------------------
/CleverDock/Models/SettingsModel.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Patterns;
2 |
3 | namespace CleverDock.Models;
4 |
5 | public class SettingsModel : SerializableModelBase
6 | {
7 | public const string SETTINGS_VERSION = "0.4.1";
8 | public const string SETTINGS_FILE = "config.json";
9 |
10 | public string Version = SETTINGS_VERSION;
11 |
12 | public SettingsModel()
13 | {
14 | IconSize = 40;
15 | CollapseDuration = 0.2;
16 | HotspotHeight = 20;
17 | DockEdgeSpacing = 20;
18 | DockHideDuration = 0.5;
19 | DockShowDuration = 0.3;
20 | DockHideDelay = 0;
21 | DockShowDelay = 0;
22 | AutoHide = true;
23 | SaveAutomatically = true;
24 | RemoveTaskbar = true;
25 | ReserveScreenSpace = false;
26 | ShowWidgets = true;
27 | Icons = new List();
28 | //Theme = ThemeModel.DefaultTheme;
29 | // Load properties from file.
30 | LoadFromFile(SETTINGS_FILE);
31 | }
32 |
33 | public double CollapseDuration { get; set; }
34 | public double DockHideDuration { get; set; }
35 | public double DockShowDuration { get; set; }
36 | public double DockHideDelay { get; set; }
37 | public double DockShowDelay { get; set; }
38 | public int HotspotHeight { get; set; }
39 | public int DockEdgeSpacing { get; set; }
40 | public int IconSize { get; set; }
41 | public ThemeModel Theme { get; set; }
42 | public List Icons { get; set; }
43 | public bool SaveAutomatically { get; set; }
44 | public bool ReserveScreenSpace { get; set; }
45 | public bool RemoveTaskbar { get; set; }
46 | public bool AutoHide { get; set; }
47 | public bool ShowWidgets { get; set; }
48 | public int OuterIconWidth { get; set; }
49 | public int OuterIconHeight { get; set; }
50 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/TaskbarManager.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Text;
3 | using CleverDock.Interop;
4 |
5 | namespace CleverDock.Managers;
6 |
7 | public static class TaskbarManager
8 | {
9 | private static IntPtr vistaStartMenuWnd = IntPtr.Zero;
10 |
11 | public static void SetTaskbarVisibility(bool visible)
12 | {
13 | var taskBarHwnd = WindowInterop.FindWindow("Shell_TrayWnd", "");
14 | var startHwnd = WindowInterop.FindWindowEx(taskBarHwnd, IntPtr.Zero, "Button", "Start");
15 | if (startHwnd == IntPtr.Zero)
16 | startHwnd = GetVistaStartMenuHwnd(taskBarHwnd);
17 | var showStyle = visible ? WindowInterop.ShowStyle.Show : WindowInterop.ShowStyle.Hide;
18 | WindowInterop.ShowWindow(taskBarHwnd, showStyle);
19 | WindowInterop.ShowWindow(startHwnd, showStyle);
20 | }
21 |
22 | private static IntPtr GetVistaStartMenuHwnd(IntPtr taskBarHwnd)
23 | {
24 | int procId;
25 | WindowInterop.GetWindowThreadProcessId(taskBarHwnd, out procId);
26 |
27 | var p = Process.GetProcessById(procId);
28 | if (p != null)
29 | foreach (ProcessThread t in p.Threads)
30 | WindowInterop.EnumThreadWindows(t.Id, EnumThreadWindowsCallback, IntPtr.Zero);
31 | return vistaStartMenuWnd;
32 | }
33 |
34 | private static bool EnumThreadWindowsCallback(IntPtr Hwnd, IntPtr lParam)
35 | {
36 | var buffer = new StringBuilder(256);
37 | var possibleTitles = new[] { "Start", "Démarrer" };
38 | if (WindowInterop.GetWindowText(Hwnd, buffer, buffer.Capacity) > 0)
39 | if (possibleTitles.Contains(buffer.ToString()))
40 | {
41 | vistaStartMenuWnd = Hwnd;
42 | return false;
43 | }
44 |
45 | return true;
46 | }
47 | }
--------------------------------------------------------------------------------
/CleverDock/Content/Effects/BrightnessEffect.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Media;
3 | using System.Windows.Media.Effects;
4 |
5 | namespace CleverDock.Effects;
6 |
7 | public class BrightnessEffect : ShaderEffect
8 | {
9 | // Dependencies
10 | public static readonly DependencyProperty InputProperty =
11 | RegisterPixelShaderSamplerProperty("Input", typeof(BrightnessEffect), 0);
12 |
13 | public static readonly DependencyProperty BrightnessProperty =
14 | DependencyProperty.Register("Brightness", typeof(double), typeof(BrightnessEffect),
15 | new UIPropertyMetadata(0.0d, PixelShaderConstantCallback(0)));
16 |
17 | public static readonly DependencyProperty ContrastProperty =
18 | DependencyProperty.Register("Contrast", typeof(double), typeof(BrightnessEffect),
19 | new UIPropertyMetadata(0.0d, PixelShaderConstantCallback(1)));
20 |
21 | // Shader
22 | private static readonly PixelShader m_pixelshader =
23 | new()
24 | {
25 | UriSource = new Uri("pack://application:,,,/CleverDock;component/Content/Effects/BrightnessEffect.ps")
26 | };
27 |
28 | public BrightnessEffect()
29 | {
30 | PixelShader = m_pixelshader;
31 | UpdateShaderValue(InputProperty);
32 | UpdateShaderValue(BrightnessProperty);
33 | UpdateShaderValue(ContrastProperty);
34 | }
35 |
36 | // Properties
37 | public Brush Input
38 | {
39 | get => (Brush)GetValue(InputProperty);
40 | set => SetValue(InputProperty, value);
41 | }
42 |
43 | public double Brightness
44 | {
45 | get => (double)GetValue(BrightnessProperty);
46 | set => SetValue(BrightnessProperty, value);
47 | }
48 |
49 | public double Contrast
50 | {
51 | get => (double)GetValue(BrightnessProperty);
52 | set => SetValue(BrightnessProperty, value);
53 | }
54 | }
--------------------------------------------------------------------------------
/CleverDock/Interop/Win32Window.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using CleverDock.Managers;
3 |
4 | namespace CleverDock.Interop;
5 |
6 | public class Win32Window
7 | {
8 | public Win32Window(IntPtr _hwnd)
9 | {
10 | Hwnd = _hwnd;
11 | }
12 |
13 | public IntPtr Hwnd { get; set; }
14 |
15 | public string FileName => ProcessManager.GetExecutablePath(ProcessId);
16 |
17 | public int ProcessId
18 | {
19 | get
20 | {
21 | int procId;
22 | WindowInterop.GetWindowThreadProcessId(Hwnd, out procId);
23 | return procId;
24 | }
25 | }
26 |
27 | public bool IsActive => Hwnd == WindowManager.Manager.ActiveWindow;
28 |
29 | public bool IsChild => ParentHwnd != IntPtr.Zero && OwnerHwnd != IntPtr.Zero;
30 |
31 | public IntPtr ParentHwnd => WindowInterop.GetParent(Hwnd);
32 |
33 | public IntPtr OwnerHwnd => WindowInterop.GetWindow(Hwnd, WindowInterop.GW_OWNER);
34 |
35 | public bool IsMinimized => WindowInterop.IsIconic(Hwnd);
36 |
37 | public string Title
38 | {
39 | get
40 | {
41 | var builder = new StringBuilder(200);
42 | WindowInterop.GetWindowText(Hwnd, builder, builder.Capacity);
43 | return builder.ToString();
44 | }
45 | }
46 |
47 | public string ClassName
48 | {
49 | get
50 | {
51 | var builder = new StringBuilder(200);
52 | WindowInterop.GetClassName(Hwnd, builder, builder.Capacity);
53 | return builder.ToString();
54 | }
55 | }
56 |
57 | public void Toggle()
58 | {
59 | if (IsActive)
60 | Minimize();
61 | else
62 | Restore();
63 | }
64 |
65 | public void Minimize()
66 | {
67 | WindowInterop.ShowWindow(Hwnd, WindowInterop.ShowStyle.Minimize);
68 | if (IsActive)
69 | WindowManager.Manager.ActiveWindow = IntPtr.Zero;
70 | }
71 |
72 | public void Restore()
73 | {
74 | WindowInterop.SetForegroundWindow(Hwnd);
75 | if (IsMinimized)
76 | WindowInterop.ShowWindow(Hwnd, WindowInterop.ShowStyle.Restore);
77 | }
78 |
79 | public void Close()
80 | {
81 | WindowInterop.SendMessage(Hwnd, WindowMessage.CLOSE, 0, 0);
82 | }
83 | }
--------------------------------------------------------------------------------
/CleverDock/Tools/AnimationTools.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Media.Animation;
3 |
4 | namespace CleverDock.Tools;
5 |
6 | public class AnimationTools
7 | {
8 | public static void FadeOut(double seconds, FrameworkElement element, double target = 0, Action completed = null)
9 | {
10 | var fadeOut = new DoubleAnimation
11 | {
12 | From = 1,
13 | To = target,
14 | Duration = TimeSpan.FromSeconds(seconds)
15 | };
16 | BeginAnimation(fadeOut, element, UIElement.OpacityProperty, completed);
17 | }
18 |
19 | public static void Collapse(double seconds, int finalWidth, FrameworkElement element, Action completed = null,
20 | double delay = 0)
21 | {
22 | var collapse = new DoubleAnimation
23 | {
24 | To = finalWidth,
25 | Duration = TimeSpan.FromSeconds(seconds),
26 | BeginTime = TimeSpan.FromSeconds(delay),
27 | EasingFunction = new QuadraticEase()
28 | };
29 | BeginAnimation(collapse, element, FrameworkElement.WidthProperty);
30 | BeginAnimation(collapse, element, FrameworkElement.HeightProperty, completed);
31 | }
32 |
33 | public static void ExpandX(double seconds, int finalWidth, FrameworkElement element, Action completed = null,
34 | double delay = 0)
35 | {
36 | var collapse = new DoubleAnimation
37 | {
38 | To = finalWidth,
39 | Duration = TimeSpan.FromSeconds(seconds),
40 | BeginTime = TimeSpan.FromSeconds(delay),
41 | EasingFunction = new QuadraticEase()
42 | };
43 | BeginAnimation(collapse, element, FrameworkElement.WidthProperty, completed);
44 | }
45 |
46 | public static void CollapseX(double seconds, FrameworkElement element, Action completed = null)
47 | {
48 | var collapse = new DoubleAnimation
49 | {
50 | To = 0,
51 | Duration = TimeSpan.FromSeconds(seconds),
52 | EasingFunction = new QuadraticEase()
53 | };
54 | BeginAnimation(collapse, element, FrameworkElement.WidthProperty, completed);
55 | }
56 |
57 | public static void TranslateY(double seconds, double to, DependencyProperty property, FrameworkElement element,
58 | Action completed = null)
59 | {
60 | var collapse = new DoubleAnimation
61 | {
62 | To = to,
63 | Duration = TimeSpan.FromSeconds(seconds),
64 | EasingFunction = new QuadraticEase
65 | {
66 | EasingMode = EasingMode.EaseInOut
67 | }
68 | };
69 | BeginAnimation(collapse, element, property, completed);
70 | }
71 |
72 | public static void BeginAnimation(Timeline animation, FrameworkElement element, DependencyProperty property,
73 | Action completed = null)
74 | {
75 | Storyboard.SetTarget(animation, element);
76 | Storyboard.SetTargetProperty(animation, new PropertyPath(property));
77 | var sb = new Storyboard();
78 | sb.Children.Add(animation);
79 | if (completed != null)
80 | sb.Completed += (s, e) => { completed.Invoke(); };
81 | sb.Begin(element);
82 | }
83 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/MouseManager.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Runtime.InteropServices;
3 | using CleverDock.Handlers;
4 | using CleverDock.Interop;
5 | using WI = CleverDock.Interop.WindowInterop;
6 |
7 | namespace CleverDock.Managers;
8 |
9 | public class MouseManager : IDisposable
10 | {
11 | private static MouseManager manager;
12 |
13 | public MouseManager()
14 | {
15 | using (var process = Process.GetCurrentProcess())
16 | using (var module = process.MainModule)
17 | {
18 | HookCallback = HookProc;
19 | HookHandle = WI.SetWindowsHookEx(WI.HookType.WH_MOUSE_LL, HookCallback,
20 | ProcessInterop.GetModuleHandle(module.ModuleName), 0);
21 | }
22 | }
23 |
24 | private WI.HookProc HookCallback { get; }
25 | private IntPtr HookHandle { get; }
26 |
27 | public static MouseManager Manager =>
28 | // Singleton Pattern
29 | manager ?? (manager = new MouseManager());
30 |
31 | public event EventHandler MouseMoved;
32 | public event EventHandler MouseButtonUp;
33 | public event EventHandler MouseButtonDown;
34 |
35 | public IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam)
36 | {
37 | if (code < 0)
38 | return WI.CallNextHookEx(HookHandle, code, wParam, lParam);
39 | var wm = (WindowMessage)wParam;
40 | var info = Marshal.PtrToStructure(lParam);
41 | switch (wm)
42 | {
43 | case WindowMessage.MOUSEMOVE:
44 | if (MouseMoved != null)
45 | MouseMoved(this, new MouseMoveEventArgs(info.pt));
46 | break;
47 | case WindowMessage.LBUTTONUP:
48 | if (MouseButtonUp != null)
49 | MouseButtonUp(this, new MouseButtonEventArgs(MouseButton.Left, info.pt));
50 | break;
51 | case WindowMessage.LBUTTONDOWN:
52 | if (MouseButtonDown != null)
53 | MouseButtonDown(this, new MouseButtonEventArgs(MouseButton.Left, info.pt));
54 | break;
55 | case WindowMessage.RBUTTONUP:
56 | if (MouseButtonUp != null)
57 | MouseButtonUp(this, new MouseButtonEventArgs(MouseButton.Right, info.pt));
58 | break;
59 | case WindowMessage.RBUTTONDOWN:
60 | if (MouseButtonDown != null)
61 | MouseButtonDown(this, new MouseButtonEventArgs(MouseButton.Right, info.pt));
62 | break;
63 | }
64 |
65 | return WI.CallNextHookEx(HookHandle, code, wParam, lParam);
66 | }
67 |
68 | #region IDisposable Support
69 |
70 | private bool disposedValue; // To detect redundant calls
71 |
72 | protected virtual void Dispose(bool disposing)
73 | {
74 | if (disposedValue)
75 | return;
76 | WI.UnhookWindowsHookEx(HookHandle);
77 | disposedValue = true;
78 | }
79 |
80 | ~MouseManager()
81 | {
82 | Dispose(false);
83 | }
84 |
85 | public void Dispose()
86 | {
87 | Dispose(true);
88 | GC.SuppressFinalize(this);
89 | }
90 |
91 | #endregion
92 | }
--------------------------------------------------------------------------------
/CleverDock/Views/WidgetsWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Timers;
2 | using System.Windows;
3 | using System.Windows.Interop;
4 | using CleverDock.Helpers;
5 | using CleverDock.Interop;
6 | using CleverDock.Managers;
7 | using Microsoft.Win32;
8 | using Timer = System.Timers.Timer;
9 |
10 | namespace CleverDock;
11 |
12 | ///
13 | /// Interaction logic for WidgetsWindow.xaml
14 | ///
15 | public partial class WidgetsWindow : Window
16 | {
17 | public WidgetsWindow()
18 | {
19 | InitializeComponent();
20 | ShowInTaskbar = false;
21 | Loaded += WidgetsWindow_Loaded;
22 | WindowManager.Manager.ActiveWindowChanged += Manager_ActiveWindowChanged;
23 | SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
24 |
25 | timer = new Timer();
26 | timer.AutoReset = false;
27 | timer.Elapsed += timer_Elapsed;
28 | StartTimer();
29 | UpdateClock();
30 | }
31 |
32 | private Timer timer { get; }
33 |
34 | private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
35 | {
36 | Dispatcher.Invoke(() =>
37 | {
38 | SetDimensions();
39 | SetTopmost();
40 | });
41 | }
42 |
43 | public static void SetWindowExTransparent(IntPtr hwnd)
44 | {
45 | var extendedStyle = (int)WindowInterop.GetWindowLongPtr(hwnd, WindowInterop.GWL_EXSTYLE);
46 | WindowInterop.SetWindowLongPtr(hwnd, WindowInterop.GWL_EXSTYLE,
47 | extendedStyle | WindowInterop.WS_EX_TRANSPARENT);
48 | }
49 |
50 | private void WidgetsWindow_Loaded(object sender, RoutedEventArgs e)
51 | {
52 | SetDimensions();
53 | }
54 |
55 | private void SetDimensions()
56 | {
57 | var resolution = ScreenHelper.GetScreenResolution();
58 | SetWindowExTransparent(new WindowInteropHelper(this).Handle);
59 | Left = resolution.Width - ActualWidth;
60 | Top = resolution.Height - ActualHeight;
61 | }
62 |
63 | private void Manager_ActiveWindowChanged(object sender, EventArgs e)
64 | {
65 | // Set Topmost when the active window changes to keep topmost position.
66 | SetTopmost();
67 | }
68 |
69 | private void timer_Elapsed(object sender, ElapsedEventArgs e)
70 | {
71 | UpdateClock();
72 | StartTimer();
73 | }
74 |
75 | private void UpdateClock()
76 | {
77 | Dispatcher.Invoke(() => { Clock.Content = string.Format("{0:HH:mm}", DateTime.Now); });
78 | }
79 |
80 | private void StartTimer()
81 | {
82 | timer.Interval = GetInterval();
83 | timer.Start();
84 | }
85 |
86 | private double GetInterval()
87 | {
88 | var now = DateTime.Now;
89 | return (60 - now.Second) * 1000 - now.Millisecond;
90 | }
91 |
92 | public void SetTopmost()
93 | {
94 | Application.Current.Dispatcher.Invoke(() =>
95 | {
96 | // Uses interop to place the window at topmost position.
97 | var hwnd = new WindowInteropHelper(this).Handle;
98 | WindowInterop.SetWindowPos(hwnd, WindowInterop.HWND_BOTTOM, 0, 0, 0, 0,
99 | WindowInterop.SWP_NOMOVE | WindowInterop.SWP_NOSIZE);
100 | });
101 | }
102 | }
--------------------------------------------------------------------------------
/CleverDock/Models/ViewModels/ThemeSettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | using CleverDock.Models;
2 | using CleverDock.Patterns;
3 |
4 | namespace CleverDock.ViewModels;
5 |
6 | public class ThemeSettingsViewModel : ViewModelBase
7 | {
8 | public static ThemeSettingsViewModel DefaultThemeSettings = new()
9 | {
10 | IconPaddingX = 15,
11 | IconPaddingY = 20
12 | };
13 |
14 | private string _author;
15 |
16 | private string _description;
17 |
18 | private int _iconPaddingX;
19 |
20 | private int _iconPaddingY;
21 |
22 | private string _themeName;
23 |
24 | private string _version;
25 |
26 | public ThemeSettingsViewModel()
27 | {
28 | }
29 |
30 | public ThemeSettingsViewModel(ThemeSettingsModel model)
31 | {
32 | ThemeName = model.ThemeName;
33 | Author = model.Author;
34 | Version = model.Version;
35 | Description = model.Description;
36 | IconPaddingX = model.IconPaddingX;
37 | IconPaddingY = model.IconPaddingY;
38 | }
39 |
40 | ///
41 | /// Name of the Theme.
42 | ///
43 | public string ThemeName
44 | {
45 | get => _themeName;
46 | set
47 | {
48 | if (value != _themeName)
49 | {
50 | _themeName = value;
51 | OnPropertyChanged();
52 | }
53 | }
54 | }
55 |
56 | ///
57 | /// Author of the Theme.
58 | ///
59 | public string Author
60 | {
61 | get => _author;
62 | set
63 | {
64 | if (value != _author)
65 | {
66 | _author = value;
67 | OnPropertyChanged();
68 | }
69 | }
70 | }
71 |
72 | ///
73 | /// Version of the Theme.
74 | ///
75 | public string Version
76 | {
77 | get => _version;
78 | set
79 | {
80 | if (value != _version)
81 | {
82 | _version = value;
83 | OnPropertyChanged();
84 | }
85 | }
86 | }
87 |
88 | ///
89 | /// Description of the Theme.
90 | ///
91 | public string Description
92 | {
93 | get => _description;
94 | set
95 | {
96 | if (value != _description)
97 | {
98 | _description = value;
99 | OnPropertyChanged();
100 | }
101 | }
102 | }
103 |
104 | ///
105 | /// X icon padding.
106 | ///
107 | public int IconPaddingX
108 | {
109 | get => _iconPaddingX;
110 | set
111 | {
112 | if (value != _iconPaddingX)
113 | {
114 | _iconPaddingX = value;
115 | OnPropertyChanged();
116 | }
117 | }
118 | }
119 |
120 | ///
121 | /// Y icon padding
122 | ///
123 | public int IconPaddingY
124 | {
125 | get => _iconPaddingY;
126 | set
127 | {
128 | if (value != _iconPaddingY)
129 | {
130 | _iconPaddingY = value;
131 | OnPropertyChanged();
132 | }
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/ThemeManager.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Windows;
3 | using CleverDock.Handlers;
4 | using CleverDock.Models;
5 | using CleverDock.Patterns;
6 | using CleverDock.Tools;
7 | using CleverDock.ViewModels;
8 | using Newtonsoft.Json;
9 |
10 | namespace CleverDock.Managers;
11 |
12 | public class ThemeManager
13 | {
14 | private static ThemeManager _manager;
15 |
16 | public static ThemeModel DefaultTheme = new()
17 | {
18 | Name = "Default - Flat",
19 | Path = "/Cleverdock;component/Themes/Flat/style.xaml"
20 | };
21 |
22 | private ResourceDictionary theme;
23 |
24 | public static ThemeManager Manager
25 | {
26 | get
27 | {
28 | if (_manager == null)
29 | _manager = new ThemeManager();
30 | return _manager;
31 | }
32 | }
33 |
34 | public event EventHandler ThemeChanged;
35 | public event EventHandler ThemeListChanged;
36 |
37 | public void ThemeWindow()
38 | {
39 | LoadTheme(VMLocator.Main.Theme);
40 | }
41 |
42 | public ThemeSettingsViewModel LoadTheme(ThemeModel theme)
43 | {
44 | ThemeSettingsViewModel result = null;
45 | try
46 | {
47 | if (theme == null)
48 | theme = DefaultTheme;
49 | if (theme.Path.StartsWith("/Cleverdock;component/"))
50 | {
51 | LoadComponentTheme(theme.Path);
52 | }
53 | else
54 | {
55 | var xamlPath = Path.Combine(theme.Path, "style.xaml");
56 | var xaml = XamlHelper.LoadXaml(xamlPath);
57 | LoadResourceDictionary(xaml);
58 | var settingsPath = Path.Combine(theme.Path, "theme.json");
59 | result = GetSettings(settingsPath);
60 | }
61 |
62 | VMLocator.Main.Theme = theme;
63 | if (ThemeChanged != null)
64 | ThemeChanged(this, new ThemeEventArgs(theme));
65 | }
66 | catch (Exception ex)
67 | {
68 | MessageBox.Show("Theme \"" + theme.Name + "\" failed to load. \n" + ex.Message);
69 | LoadTheme(DefaultTheme);
70 | }
71 |
72 | return result;
73 | }
74 |
75 | public ThemeSettingsViewModel GetSettings(string path)
76 | {
77 | if (!File.Exists(path))
78 | return ThemeSettingsViewModel.DefaultThemeSettings;
79 | using (var stream = new StreamReader(path))
80 | {
81 | try
82 | {
83 | return JsonConvert.DeserializeObject(stream.ReadToEnd());
84 | }
85 | catch (InvalidOperationException ex)
86 | {
87 | Console.WriteLine(ex.Message);
88 | return ThemeSettingsViewModel.DefaultThemeSettings;
89 | }
90 | }
91 | }
92 |
93 | public void LoadComponentTheme(string path)
94 | {
95 | var uri = new Uri(path, UriKind.Relative);
96 | var theme = Application.LoadComponent(uri) as ResourceDictionary;
97 | LoadResourceDictionary(theme);
98 | }
99 |
100 | public void LoadResourceDictionary(ResourceDictionary resourceDict)
101 | {
102 | if (theme != null)
103 | Application.Current.Resources.MergedDictionaries.Remove(theme);
104 | Application.Current.Resources.MergedDictionaries.Add(resourceDict);
105 | }
106 | }
--------------------------------------------------------------------------------
/CleverDock/Models/Patterns/ActionCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Input;
2 |
3 | namespace CleverDock.Patterns;
4 |
5 | public class ActionCommand : ICommand
6 | {
7 | private readonly Func _canExecute;
8 |
9 | private readonly Action _execute;
10 |
11 | private bool _couldExecute;
12 |
13 | public ActionCommand(Action execute, Func canExecute = null)
14 | {
15 | _execute = execute;
16 | _canExecute = canExecute ?? (x => true);
17 | _couldExecute = true;
18 | }
19 |
20 | public event EventHandler CanExecuteChanged;
21 |
22 | bool ICommand.CanExecute(object parameter)
23 | {
24 | try
25 | {
26 | var canExecute = CanExecute((T)parameter);
27 |
28 | if (_couldExecute ^ canExecute)
29 | {
30 | _couldExecute = canExecute;
31 | OnCanExecuteChanged();
32 | }
33 |
34 | return canExecute;
35 | }
36 | catch (InvalidCastException)
37 | {
38 | if (_couldExecute)
39 | {
40 | _couldExecute = false;
41 | OnCanExecuteChanged();
42 | }
43 |
44 | return false;
45 | }
46 | }
47 |
48 | void ICommand.Execute(object parameter)
49 | {
50 | Execute((T)parameter);
51 | }
52 |
53 | public static implicit operator ActionCommand(Action execute)
54 | {
55 | return new ActionCommand(execute);
56 | }
57 |
58 | public bool CanExecute(T parameter)
59 | {
60 | return _canExecute(parameter);
61 | }
62 |
63 | public void Execute(T parameter)
64 | {
65 | _execute(parameter);
66 | }
67 |
68 | private void OnCanExecuteChanged()
69 | {
70 | var handler = CanExecuteChanged;
71 |
72 | if (handler != null) handler(this, new EventArgs());
73 | }
74 | }
75 |
76 | public class ActionCommand : ICommand
77 | {
78 | private readonly Func _canExecute;
79 |
80 | private readonly Action _execute;
81 |
82 | private bool _couldExecute;
83 |
84 | public ActionCommand(Action execute, Func canExecute = null)
85 | {
86 | _execute = execute;
87 | _canExecute = canExecute ?? (() => true);
88 | _couldExecute = true;
89 | }
90 |
91 | public event EventHandler CanExecuteChanged;
92 |
93 | bool ICommand.CanExecute(object parameter)
94 | {
95 | try
96 | {
97 | var canExecute = CanExecute();
98 |
99 | if (_couldExecute ^ canExecute)
100 | {
101 | _couldExecute = canExecute;
102 | OnCanExecuteChanged();
103 | }
104 |
105 | return canExecute;
106 | }
107 | catch (InvalidCastException)
108 | {
109 | if (_couldExecute)
110 | {
111 | _couldExecute = false;
112 | OnCanExecuteChanged();
113 | }
114 |
115 | return false;
116 | }
117 | }
118 |
119 | void ICommand.Execute(object parameter)
120 | {
121 | Execute();
122 | }
123 |
124 | public static implicit operator ActionCommand(Action execute)
125 | {
126 | return new ActionCommand(execute);
127 | }
128 |
129 | public bool CanExecute()
130 | {
131 | return _canExecute();
132 | }
133 |
134 | public void Execute()
135 | {
136 | _execute();
137 | }
138 |
139 | private void OnCanExecuteChanged()
140 | {
141 | var handler = CanExecuteChanged;
142 |
143 | if (handler != null) handler(this, new EventArgs());
144 | }
145 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CleverDock
2 | ===========
3 |
4 | A simple dock for Windows which features extensive theming capabilities and blurry
5 | reflections. The dock is fully compatible with Windows Vista/7/8/8.1.
6 |
7 | ***I have stopped maintaning this project. I welcome forks and will accept pull requests.***
8 |
9 | This software is still an early alpha and may contain bugs.
10 |
11 | 
12 |
13 | What's new in 0.4.0
14 | -------------------
15 | - Major optimization of the window: the dock is now running at 60FPS!
16 | - Windows demanding attention now bounce on the dock, even when hidden.
17 | - Added settings for tweaking X and Y padding of icons.
18 | - Dock does hide anymore when the mouse is between icons, or in a context menu.
19 |
20 | What's new in 0.3.0
21 | -------------------
22 | - Optimized overall dock performance for smoother animations.
23 | - Added collapsing animations when icons are dragged.
24 | - Better theme loading error management.
25 | - Fixed a crash when a window does not have a file name.
26 | - Added the AutoHide feature.
27 | - Fixed a bug with the *Reserve screen space* checkbox.
28 |
29 | What's new in 0.2.0
30 | -------------------
31 | - Eliminated missing blurred icons.
32 | - Enhanced window detection algorithms (no more missing windows hopefully).
33 | - New theme engine! Loads uncompiled .xaml files under Themes/ folder which can be modified to your liking.
34 | - New Dark version of the default "Metal" theme.
35 | - Settings file now in .json format for better readability and performance.
36 | - Bug fixes in the dock icons context menus.
37 | - Better screen size detection and dock placement.
38 | - Added an option to reserve a screen edge for the dock.
39 | - Themes update instantly as the .xaml files are saved.
40 |
41 | Download
42 | --------
43 | Version 0.4.0 Standalone - Vista/7/8/8.1:
44 | [CleverDock-v0.4.0.zip](https://github.com/ldom66/clever-dock/releases/download/v0.4.0/CleverDock-v0.4.0.zip)
45 |
46 | Version 0.3.0 Standalone - Vista/7/8/8.1:
47 | [CleverDock-v0.3.0.zip](https://github.com/ldom66/clever-dock/releases/download/v0.3.0/CleverDock-v0.3.0.zip)
48 |
49 | Version 0.2.0 Standalone - Vista/7/8/8.1:
50 | [CleverDock-v0.2.0.zip](https://github.com/ldom66/clever-dock/releases/download/v0.2.0/CleverDock-v0.2.0.zip)
51 |
52 | How to install
53 | --------------
54 | 1. Download the zip above
55 | 2. uncompress it somewhere on your computer *- C:\CleverDock\ For example -*.
56 | 3. Execute the file named **CleverDock.exe**
57 |
58 | Troubleshooting
59 | ---------------
60 | ##### The dock worked once but now it crashes everytime i try to open it.
61 | > There may be an issue with your configuration file. Try to erase config.json and restart the dock.
62 |
63 | ##### I closed the dock from task manager / The dock crashed and now I have no task bar.
64 | > Start CleverDock using the task manager (in the File menu and then Start). Then right-click on CleverDock and click "Exit CleverDock"
65 | > If that did not work, open the task manager and close explorer, then use the file menu to start explorer.exe again.
66 |
67 | ##### CleverDock can not start, it crashes as soon as I try to open it.
68 | > You may not have .Net Framework 4.5. Install it using [this link.](http://www.microsoft.com/fr-ca/download/details.aspx?id=30653)
69 |
70 | ##### I can not restore/minimize/close a specific application on the dock.
71 | > The app you can't use may have been started as an administrator. The dock must have administrator rights to control these processes. Simply exit CleverDock then start it again by right clicking and selecting *Run as administrator*. You can also check the box *Run this program as an administrator* in the executable's properties window in the *Compatibility* tab.
72 |
--------------------------------------------------------------------------------
/CleverDock/CleverDock.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net8.0-windows
6 | enable
7 | enable
8 | true
9 | 0.5.0
10 | CleverDock
11 | Dominic Lacaille
12 | A simple dock for Windows which features extensive theming capabilities and blurry reflections.
13 | https://github.com/dlacaille/clever-dock
14 | https://github.com/dlacaille/clever-dock
15 | git
16 |
17 |
18 |
19 |
20 | MSBuild:Compile
21 | Designer
22 |
23 |
24 | MainWindow.xaml
25 | Code
26 |
27 |
28 | Designer
29 | MSBuild:Compile
30 |
31 |
32 | Designer
33 | MSBuild:Compile
34 |
35 |
36 | Designer
37 | MSBuild:Compile
38 |
39 |
40 |
41 |
42 |
43 | Always
44 |
45 |
46 | Designer
47 | Always
48 |
49 |
50 | Designer
51 | Always
52 |
53 |
54 | Always
55 |
56 |
57 | Always
58 |
59 |
60 | Designer
61 | Always
62 |
63 |
64 | Always
65 |
66 |
67 | Always
68 |
69 |
70 | Always
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/CleverDock/Interop/ProcessInterop.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using System.Text;
3 |
4 | namespace CleverDock.Interop;
5 |
6 | internal class ProcessInterop
7 | {
8 | ///
9 | /// Required to retrieve certain information about a process
10 | ///
11 | public const int PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
12 |
13 | ///
14 | /// Retrieves the full name of the executable image for the specified process.
15 | ///
16 | ///
17 | /// A handle to the process. This handle must be created with the PROCESS_QUERY_INFORMATION or
18 | /// PROCESS_QUERY_LIMITED_INFORMATION access right. For more information, see Process Security and Access Rights.
19 | ///
20 | ///
21 | /// 0: The name should use the Win32 path format. 1: The name should use the native system path
22 | /// format.
23 | ///
24 | /// The path to the executable image. If the function succeeds, this string is null-terminated.
25 | ///
26 | /// On input, specifies the size of the lpExeName buffer, in characters. On success, receives the number
27 | /// of characters written to the buffer, not including the null-terminating character.
28 | ///
29 | /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.
30 | [DllImport("kernel32.dll")]
31 | public static extern bool QueryFullProcessImageName(IntPtr hprocess, int dwFlags,
32 | StringBuilder lpExeName, out int size);
33 |
34 | ///
35 | /// Opens an existing local process object.
36 | ///
37 | ///
38 | /// The access to the process object. This access right is checked against the security
39 | /// descriptor for the process. This parameter can be one or more of the process access rights.
40 | ///
41 | ///
42 | /// If this value is TRUE, processes created by this process will inherit the handle.
43 | /// Otherwise, the processes do not inherit this handle.
44 | ///
45 | /// The identifier of the local process to be opened.
46 | /// If the function succeeds, the return value is an open handle to the specified process.
47 | [DllImport("kernel32.dll")]
48 | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
49 |
50 | ///
51 | /// Closes an open object handle.
52 | ///
53 | /// A valid handle to an open object.
54 | /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.
55 | [DllImport("kernel32.dll", SetLastError = true)]
56 | public static extern bool CloseHandle(IntPtr hHandle);
57 |
58 | ///
59 | /// Retrieves a module handle for the specified module. The module must have been loaded by the calling process.
60 | ///
61 | ///
62 | /// The name of the loaded module (either a .dll or .exe file). If the file name extension is
63 | /// omitted, the default library extension .dll is appended. The file name string can include a trailing point
64 | /// character (.) to indicate that the module name has no extension. The string does not have to specify a path. When
65 | /// specifying a path, be sure to use backslashes (\), not forward slashes (/). The name is compared (case
66 | /// independently) to the names of modules currently mapped into the address space of the calling process.
67 | ///
68 | ///
69 | /// If the function succeeds, the return value is a handle to the specified module. If the function fails, the
70 | /// return value is NULL.To get extended error information, call GetLastError.
71 | ///
72 | [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
73 | public static extern IntPtr GetModuleHandle(string lpModuleName);
74 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/IconManager.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Runtime.InteropServices;
3 | using System.Windows;
4 | using System.Windows.Interop;
5 | using System.Windows.Media.Imaging;
6 | using CleverDock.Interop;
7 | using WI = CleverDock.Interop.WindowInterop;
8 | using II = CleverDock.Interop.IconInterop;
9 |
10 | namespace CleverDock.Managers;
11 |
12 | public class IconManager
13 | {
14 | private static BitmapImage unknownIcon;
15 |
16 | public static BitmapSource UnknownIcon
17 | {
18 | get
19 | {
20 | if (unknownIcon == null)
21 | unknownIcon =
22 | new BitmapImage(new Uri("pack://application:,,,/CleverDock;component/Content/unknown.png"));
23 | return unknownIcon;
24 | }
25 | }
26 |
27 | private static BitmapSource IconSource(IntPtr handle)
28 | {
29 | var result = Imaging.CreateBitmapSourceFromHIcon(handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
30 | result.Freeze();
31 | return result;
32 | }
33 |
34 | public static BitmapSource GetAppIcon(IntPtr hwnd)
35 | {
36 | var hIcon = WI.GetClassLongPtr(hwnd, WI.ICON_SMALL);
37 | try
38 | {
39 | if (hIcon == IntPtr.Zero)
40 | hIcon = WI.SendMessage(hwnd, WindowMessage.GETICON, WI.ICON_SMALL2, 0);
41 | if (hIcon == IntPtr.Zero)
42 | hIcon = WI.SendMessage(hwnd, WindowMessage.GETICON, WI.ICON_BIG, 0);
43 | if (hIcon == IntPtr.Zero)
44 | hIcon = WI.GetClassLongPtr(hwnd, WI.GCL_HICON);
45 | if (hIcon == IntPtr.Zero)
46 | hIcon = WI.GetClassLongPtr(hwnd, WI.GCL_HICONSM);
47 | }
48 | catch (Exception ex)
49 | {
50 | }
51 |
52 | if (hIcon == IntPtr.Zero)
53 | return null;
54 | var bs = IconSource(hIcon);
55 | return bs;
56 | }
57 |
58 | public static BitmapSource GetSmallIcon(string FileName, bool small)
59 | {
60 | var shinfo = new IconInterop.SHFILEINFO();
61 | uint flags;
62 |
63 | if (small)
64 | flags = II.SHGFI_ICON | II.SHGFI_SMALLICON;
65 | else
66 | flags = II.SHGFI_ICON | II.SHGFI_LARGEICON;
67 |
68 | var res = II.SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), flags);
69 |
70 | if (res == 0)
71 | throw new FileNotFoundException();
72 |
73 | var bs = IconSource(shinfo.hIcon);
74 |
75 | bs.Freeze(); // very important to avoid memory leak
76 | II.DestroyIcon(shinfo.hIcon);
77 |
78 | return bs;
79 | }
80 |
81 | public static BitmapSource GetIcon(string path, int size)
82 | {
83 | if (size <= 16)
84 | return GetSmallIcon(path, true);
85 | if (size <= 32)
86 | return GetSmallIcon(path, false);
87 | if (size <= 48)
88 | return GetLargeIcon(path, false);
89 | return GetLargeIcon(path, true);
90 | }
91 |
92 | public static BitmapSource GetLargeIcon(string path, bool jumbo)
93 | {
94 | var shinfo = new IconInterop.SHFILEINFO();
95 | const uint SHGFI_SYSICONINDEX = 0x4000;
96 | const int FILE_ATTRIBUTE_NORMAL = 0x80;
97 | var flags = SHGFI_SYSICONINDEX;
98 |
99 | var res = II.SHGetFileInfo(path, FILE_ATTRIBUTE_NORMAL, ref shinfo, Marshal.SizeOf(shinfo), flags);
100 |
101 | if (res == 0)
102 | return null;
103 |
104 | var iconIndex = shinfo.iIcon;
105 |
106 | // Get the System IImageList object from the Shell:
107 | var iidImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
108 |
109 | IconInterop.IImageList iml;
110 | var size = jumbo ? II.SHIL_JUMBO : II.SHIL_EXTRALARGE;
111 | II.SHGetImageList(size, ref iidImageList, out iml);
112 | var hIcon = IntPtr.Zero;
113 | const int ILD_TRANSPARENT = 1;
114 | iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon);
115 |
116 | var bs = IconSource(hIcon);
117 |
118 | bs.Freeze(); // very important to avoid memory leak
119 | II.DestroyIcon(hIcon);
120 | II.SendMessage(hIcon, II.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
121 |
122 | return bs;
123 | }
124 | }
--------------------------------------------------------------------------------
/CleverDock/Behaviors/DockAutoHideBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Windows;
3 | using System.Windows.Media;
4 | using System.Windows.Media.Animation;
5 | using CleverDock.Handlers;
6 | using CleverDock.Helpers;
7 | using CleverDock.Managers;
8 | using CleverDock.Patterns;
9 | using Microsoft.Xaml.Behaviors;
10 |
11 | namespace CleverDock.Behaviors;
12 |
13 | public class DockAutoHideBehavior : Behavior
14 | {
15 | private const double HotspotHeight = 10;
16 |
17 | private bool Hidden;
18 | private Storyboard Storyboard;
19 |
20 | private Rect MouseHotspot
21 | {
22 | get
23 | {
24 | var window = DockManager.Manager.Window;
25 | var resolution = ScreenHelper.GetScreenResolution();
26 | var hotspotWidth = Math.Max(window.Width, resolution.Width / 2);
27 | var hotspotLeft = (resolution.Width - hotspotWidth) / 2;
28 | var hotspotHeight = Hidden ? window.Height : HotspotHeight;
29 | return new Rect(hotspotLeft, resolution.Height - hotspotHeight, hotspotWidth, hotspotHeight);
30 | }
31 | }
32 |
33 | protected override void OnAttached()
34 | {
35 | base.OnAttached();
36 | WindowManager.Manager.ActiveWindowRectChanged += Manager_ActiveWindowRectChanged;
37 | MouseManager.Manager.MouseMoved += Manager_MouseMoved;
38 | VMLocator.Main.PropertyChanged += Main_PropertyChanged;
39 | }
40 |
41 | private void Manager_MouseMoved(object sender, MouseMoveEventArgs e)
42 | {
43 | if (!VMLocator.Main.AutoHide)
44 | return;
45 | Application.Current.Dispatcher.Invoke(() =>
46 | {
47 | if (Hidden && MouseHotspot.Contains(e.CursorPosition))
48 | Show();
49 | });
50 | }
51 |
52 | private void Main_PropertyChanged(object sender, PropertyChangedEventArgs e)
53 | {
54 | // If the dock is hidden an AutoHide is set to false, show it.
55 | if (e.PropertyName == "AutoHide")
56 | if (Hidden && !VMLocator.Main.AutoHide)
57 | Show();
58 | }
59 |
60 | private void Manager_ActiveWindowRectChanged(object sender, WindowRectEventArgs e)
61 | {
62 | if (!VMLocator.Main.AutoHide)
63 | return;
64 | Application.Current.Dispatcher.Invoke(() => { HideOrShowDock(); });
65 | }
66 |
67 | private void HideOrShowDock()
68 | {
69 | var window = DockManager.Manager.Window;
70 | var dockPos = AssociatedObject.PointToScreen(new Point(0, 0));
71 | var dockHeight = AssociatedObject.ActualHeight;
72 | var dockRect = new Rect(dockPos.X, window.Top + window.Height - dockHeight, AssociatedObject.ActualWidth,
73 | dockHeight);
74 | var intersects = WindowManager.Manager.ActiveWindowRect.IntersectsWith(dockRect);
75 | //Console.WriteLine("X:{0} Y:{1} W:{2} H:{3} Intersects:{4}", dockRect.Left, dockRect.Top, dockRect.Width, dockRect.Height, intersects ? "True" : "False");
76 | if (intersects)
77 | Hide();
78 | else
79 | Show();
80 | }
81 |
82 | private void AnimateY(float to, TimeSpan duration, Action completed = null)
83 | {
84 | // Stop the previous animation
85 | if (Storyboard != null)
86 | Storyboard.Stop();
87 |
88 | var translation = new DoubleAnimation(to, duration);
89 | translation.EasingFunction = new QuadraticEase
90 | {
91 | EasingMode = EasingMode.EaseInOut
92 | };
93 | Storyboard.SetTargetName(translation, "DockTransform");
94 | Storyboard.SetTargetProperty(translation, new PropertyPath(TranslateTransform.YProperty));
95 |
96 | Storyboard = new Storyboard();
97 | Storyboard.Children.Add(translation);
98 | Storyboard.Begin(AssociatedObject);
99 | if (completed != null)
100 | Storyboard.Completed += (s, e) =>
101 | {
102 | Storyboard = null;
103 | completed.Invoke();
104 | };
105 | }
106 |
107 | private void Hide()
108 | {
109 | if (Hidden)
110 | return;
111 | Hidden = true;
112 | AnimateY(100, TimeSpan.FromSeconds(0.5));
113 | }
114 |
115 | private void Show()
116 | {
117 | if (!Hidden)
118 | return;
119 | Hidden = false;
120 | AnimateY(0, TimeSpan.FromSeconds(0.5));
121 | }
122 | }
--------------------------------------------------------------------------------
/CleverDock/Managers/DockManager.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using System.Windows;
3 | using System.Windows.Interop;
4 | using CleverDock.Helpers;
5 | using CleverDock.Interop;
6 | using CleverDock.Patterns;
7 | using CleverDock.ViewModels;
8 | using Microsoft.Win32;
9 |
10 | namespace CleverDock.Managers;
11 |
12 | public class DockManager
13 | {
14 | private static DockManager manager;
15 | public Window Window;
16 |
17 | public DockManager()
18 | {
19 | WindowLoadedCommand = new ActionCommand(WindowLoadedAction);
20 | LeftMouseDownCommand = new ActionCommand(LeftMouseDownAction);
21 | LeftMouseUpCommand = new ActionCommand(LeftMouseUpAction);
22 | }
23 |
24 | public WidgetsWindow WidgetsWindow { get; set; }
25 |
26 | public static DockManager Manager =>
27 | // Singleton Pattern
28 | manager ?? (manager = new DockManager());
29 |
30 | public ActionCommand WindowLoadedCommand { get; private set; }
31 |
32 | public ActionCommand LeftMouseDownCommand { get; private set; }
33 |
34 | public ActionCommand LeftMouseUpCommand { get; private set; }
35 |
36 | private void WindowLoadedAction(Window _window)
37 | {
38 | Window = _window;
39 | var handle = new WindowInteropHelper(Window).Handle;
40 | WindowManager.Manager.SetDockHwnd(handle);
41 | WindowManager.Manager.Start();
42 | ThemeManager.Manager.ThemeWindow();
43 | if (VMLocator.Main.RemoveTaskbar)
44 | TaskbarManager.SetTaskbarVisibility(false);
45 | SetWorkingArea(VMLocator.Main.ReserveScreenSpace);
46 | // Subscribe to window events.
47 | Application.Current.Exit += Current_Exit;
48 | WindowManager.Manager.ActiveWindowChanged += Manager_ActiveWindowChanged;
49 | SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
50 | // Add a hook to receive window events from Windows.
51 | var source = PresentationSource.FromVisual(Window) as HwndSource;
52 | source.AddHook(WndProc);
53 | // These two lines are necessary to capture the window events in the above hook.
54 | WindowInterop.RegisterShellHookWindow(handle);
55 | WindowInterop.RegisterWindowMessage("SHELLHOOK");
56 | SetWindowPosition();
57 | // Show the widgets window
58 | ShowWidgets();
59 | }
60 |
61 | public void ShowWidgets()
62 | {
63 | if (WidgetsWindow == null)
64 | WidgetsWindow = new WidgetsWindow();
65 | WidgetsWindow.Show();
66 | }
67 |
68 | public void SetWorkingArea(bool reserveScrenSpace)
69 | {
70 | var reserved = reserveScrenSpace ? 100 : 0;
71 | var resolution = ScreenHelper.GetScreenResolution(true);
72 | WorkAreaManager.SetWorkingArea(0, 0, (int)resolution.Width, (int)resolution.Height - reserved);
73 | }
74 |
75 | private void LeftMouseDownAction(IconViewModel icon)
76 | {
77 | }
78 |
79 | private void LeftMouseUpAction(IconViewModel icon)
80 | {
81 | icon.Run();
82 | }
83 |
84 | private void Current_Exit(object sender, ExitEventArgs e)
85 | {
86 | if (VMLocator.Main.RemoveTaskbar)
87 | TaskbarManager.SetTaskbarVisibility(true);
88 | WindowManager.Manager.Stop();
89 | }
90 |
91 | public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
92 | {
93 | // If windows sends the HSHELL_FLASH event, a window in the taskbar is flashing.
94 | if (wParam.ToInt32() == WindowInterop.HSHELL_FLASH)
95 | // Find the window with corresponding Hwnd and bounce it.
96 | FindIcon(lParam)?.AnimateIconBounce();
97 | // If windows sends the HSHELL_GETMINRECT event, a window in the taskbar is minimizing or maximizing.
98 | if (wParam.ToInt32() == WindowInterop.HSHELL_GETMINRECT)
99 | {
100 | var param = Marshal.PtrToStructure(lParam);
101 | var icon = FindIcon(param.hWnd);
102 | if (icon == null)
103 | return IntPtr.Zero;
104 | var point = icon.Element.TransformToVisual(Window).Transform(new Point(0, 0));
105 | var rect = new WindowInterop.SRect
106 | {
107 | Bottom = (short)(point.Y + Window.Top + icon.Element.ActualHeight),
108 | Left = (short)(point.X + Window.Left),
109 | Right = (short)(point.X + Window.Left + icon.Element.ActualWidth),
110 | Top = (short)(point.Y + Window.Top)
111 | };
112 | var newParam = new WindowInterop.MinRectParam
113 | {
114 | hWnd = param.hWnd,
115 | Rect = rect
116 | };
117 | Marshal.StructureToPtr(newParam, lParam, true);
118 | handled = true;
119 | return new IntPtr(1);
120 | }
121 |
122 | return IntPtr.Zero;
123 | }
124 |
125 | public IconViewModel FindIcon(IntPtr hwnd)
126 | {
127 | return VMLocator.Main.Icons.FirstOrDefault(i => i.Windows.Any(w => w.Hwnd == hwnd));
128 | }
129 |
130 | private void SetWindowPosition()
131 | {
132 | var resolution = ScreenHelper.GetScreenResolution();
133 | Window.Left = (resolution.Width - Window.Width) / 2;
134 | Window.Top = resolution.Height - Window.Height;
135 | }
136 |
137 | private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
138 | {
139 | Application.Current.Dispatcher.Invoke(() => { SetWindowPosition(); });
140 | }
141 |
142 | private void Manager_ActiveWindowChanged(object sender, EventArgs e)
143 | {
144 | Application.Current.Dispatcher.Invoke(() => { SetTopmost(); });
145 | }
146 |
147 | private void SetTopmost()
148 | {
149 | Application.Current.Dispatcher.Invoke(() =>
150 | {
151 | // Uses interop to place the window at topmost position.
152 | var hwnd = new WindowInteropHelper(Window).Handle;
153 | WindowInterop.SetWindowPos(hwnd, WindowInterop.HWND_TOPMOST, 0, 0, 0, 0,
154 | WindowInterop.SWP_NOMOVE | WindowInterop.SWP_NOSIZE);
155 | });
156 | }
157 | }
--------------------------------------------------------------------------------
/CleverDock/Behaviors/DockIconDragBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using System.Windows.Input;
4 | using CleverDock.Handlers;
5 | using CleverDock.Managers;
6 | using CleverDock.Patterns;
7 | using CleverDock.Tools;
8 | using CleverDock.ViewModels;
9 | using Microsoft.Xaml.Behaviors;
10 | using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs;
11 |
12 | namespace CleverDock.Behaviors;
13 |
14 | public class DockIconDragBehavior : Behavior
15 | {
16 | private DraggedIconWindow draggedIconWindow;
17 | private IconViewModel icon;
18 | private bool isMouseDown;
19 | private ItemsControl itemsControl;
20 | private Point mouseStart;
21 | private double mouseX, mouseY;
22 | private IconViewModel placeholder;
23 |
24 | private Panel Panel => FrameworkHelper.GetVisualChild(itemsControl);
25 |
26 | protected override void OnAttached()
27 | {
28 | base.OnAttached();
29 | AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
30 | AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
31 | AssociatedObject.MouseMove += AssociatedObject_MouseMove;
32 | AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
33 | }
34 |
35 | private void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
36 | {
37 | DetachIcon();
38 | }
39 |
40 | private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
41 | {
42 | var pos = e.GetPosition(AssociatedObject);
43 | if (Distance(mouseStart, pos) > 10)
44 | DetachIcon();
45 | }
46 |
47 | private double Distance(Point a, Point b)
48 | {
49 | return Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2));
50 | }
51 |
52 | private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
53 | {
54 | mouseStart = e.GetPosition(AssociatedObject);
55 | isMouseDown = true;
56 | }
57 |
58 | private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
59 | {
60 | isMouseDown = false;
61 | }
62 |
63 | private void DetachIcon()
64 | {
65 | if (isMouseDown)
66 | {
67 | AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
68 | AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
69 | itemsControl = FrameworkHelper.GetParent(AssociatedObject);
70 | MouseManager.Manager.MouseMoved += Manager_MouseMoved;
71 | icon = (IconViewModel)AssociatedObject.DataContext;
72 | VMLocator.Main.Icons.Remove(icon);
73 | if (draggedIconWindow == null)
74 | draggedIconWindow = new DraggedIconWindow();
75 | draggedIconWindow.DataContext = icon;
76 | itemsControl.CaptureMouse();
77 | itemsControl.MouseUp += itemsControl_MouseUp;
78 | itemsControl.MouseMove += itemsControl_MouseMove;
79 | }
80 | }
81 |
82 | private void itemsControl_MouseUp(object sender, MouseButtonEventArgs e)
83 | {
84 | if (IsInDock(mouseX, mouseY))
85 | {
86 | // Replace placeholder with icon.
87 | var index = VMLocator.Main.Icons.IndexOf(placeholder);
88 | VMLocator.Main.Icons.Remove(placeholder);
89 | VMLocator.Main.Icons.Insert(index, icon);
90 | icon.Pinned = true;
91 | }
92 | else
93 | {
94 | VMLocator.Main.Icons.Remove(placeholder);
95 | if (icon.IsActive)
96 | VMLocator.Main.Icons.Add(icon);
97 | }
98 |
99 | // Clean up events and variables.
100 | isMouseDown = false;
101 | MouseManager.Manager.MouseMoved -= Manager_MouseMoved;
102 | itemsControl.ReleaseMouseCapture();
103 | itemsControl.MouseUp -= itemsControl_MouseUp;
104 | itemsControl.MouseMove -= itemsControl_MouseMove;
105 | var window = draggedIconWindow;
106 | AnimationTools.FadeOut(0.2, window, 0, () =>
107 | {
108 | window.Close();
109 | window = null;
110 | });
111 | icon = null;
112 | placeholder = null;
113 | }
114 |
115 | private bool IsInElement(double x, double y)
116 | {
117 | return x >= 0 && x <= AssociatedObject.ActualWidth && y >= 0 && y <= AssociatedObject.ActualHeight;
118 | }
119 |
120 | private bool IsInDock(double x, double y)
121 | {
122 | return x >= 0 && x <= itemsControl.ActualWidth && y >= 0 && y <= itemsControl.ActualHeight;
123 | }
124 |
125 | private void itemsControl_MouseMove(object sender, MouseEventArgs e)
126 | {
127 | mouseY = e.GetPosition(itemsControl).Y;
128 | mouseX = e.GetPosition(itemsControl).X;
129 | if (IsInDock(mouseX, mouseY))
130 | {
131 | var index = GetDropIndex(mouseX);
132 | if (placeholder == null)
133 | placeholder = new IconViewModel();
134 | if (VMLocator.Main.Icons.IndexOf(placeholder) != index)
135 | {
136 | VMLocator.Main.Icons.Remove(placeholder);
137 | VMLocator.Main.Icons.Insert(index, placeholder);
138 | }
139 | }
140 | else
141 | {
142 | VMLocator.Main.Icons.Remove(placeholder);
143 | placeholder = null;
144 | }
145 | }
146 |
147 | public FrameworkElement FindFrameworkElement(IconViewModel icon)
148 | {
149 | return Panel.Children.OfType().FirstOrDefault(c => c.DataContext == icon);
150 | }
151 |
152 | public int GetDropIndex(double x)
153 | {
154 | double cumul = 0;
155 | var index = 0;
156 | foreach (FrameworkElement item in Panel.Children)
157 | {
158 | var width = item.ActualWidth;
159 | if (x < cumul + width) // If mouse is before icon (before half width)
160 | return index; // Set index after last icon
161 | cumul += width;
162 | index++;
163 | }
164 |
165 | return index - 1;
166 | }
167 |
168 | private void Manager_MouseMoved(object sender, MouseMoveEventArgs e)
169 | {
170 | Application.Current.Dispatcher.Invoke(() =>
171 | {
172 | var iconSize = VMLocator.Main.IconSize;
173 | draggedIconWindow.Left = e.CursorPosition.X - iconSize / 2;
174 | draggedIconWindow.Top = e.CursorPosition.Y - iconSize / 2;
175 | if (!draggedIconWindow.IsActive)
176 | draggedIconWindow.Show();
177 | });
178 | }
179 | }
--------------------------------------------------------------------------------
/CleverDock/Models/ViewModels/MainViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Windows;
3 | using CleverDock.Managers;
4 | using CleverDock.Models;
5 | using CleverDock.Patterns;
6 |
7 | namespace CleverDock.ViewModels;
8 |
9 | public class MainViewModel : ViewModelBase
10 | {
11 | private readonly SettingsModel settings = new();
12 |
13 | public MainViewModel()
14 | {
15 | Application.Current.Exit += Current_Exit;
16 | }
17 |
18 | private void Current_Exit(object sender, ExitEventArgs e)
19 | {
20 | settings.SaveAsFile(SettingsModel.SETTINGS_FILE);
21 | }
22 |
23 | #region Commands
24 |
25 | #endregion
26 |
27 | #region Properties
28 |
29 | ///
30 | /// Duration time in seconds for the collapsing animation.
31 | ///
32 | public double CollapseDuration
33 | {
34 | get => settings.CollapseDuration;
35 | set
36 | {
37 | if (value != settings.CollapseDuration)
38 | {
39 | settings.CollapseDuration = value;
40 | OnPropertyChanged();
41 | }
42 | }
43 | }
44 |
45 | ///
46 | /// Duration time in seconds for the hiding animation.
47 | ///
48 | public double DockHideDuration
49 | {
50 | get => settings.DockHideDuration;
51 | set
52 | {
53 | if (value != settings.DockHideDuration)
54 | {
55 | settings.DockHideDuration = value;
56 | OnPropertyChanged();
57 | }
58 | }
59 | }
60 |
61 | ///
62 | /// Duration time in seconds for the show animation.
63 | ///
64 | public double DockShowDuration
65 | {
66 | get => settings.DockShowDuration;
67 | set
68 | {
69 | if (value != settings.DockShowDuration)
70 | {
71 | settings.DockShowDuration = value;
72 | OnPropertyChanged();
73 | }
74 | }
75 | }
76 |
77 | ///
78 | /// Seconds the dock should stay visible before hiding.
79 | ///
80 | public double DockHideDelay
81 | {
82 | get => settings.DockHideDelay;
83 | set
84 | {
85 | if (value != settings.DockHideDelay)
86 | {
87 | settings.DockHideDelay = value;
88 | OnPropertyChanged();
89 | }
90 | }
91 | }
92 |
93 | ///
94 | /// Seconds the mouse must stay in the hotspot before showing the dock.
95 | ///
96 | public double DockShowDelay
97 | {
98 | get => settings.DockShowDelay;
99 | set
100 | {
101 | if (value != settings.DockShowDelay)
102 | {
103 | settings.DockShowDelay = value;
104 | OnPropertyChanged();
105 | }
106 | }
107 | }
108 |
109 | ///
110 | /// Height of the hotspot at the bottom of the screen.
111 | ///
112 | public int HotspotHeight
113 | {
114 | get => settings.HotspotHeight;
115 | set
116 | {
117 | if (value != settings.HotspotHeight)
118 | {
119 | settings.HotspotHeight = value;
120 | OnPropertyChanged();
121 | }
122 | }
123 | }
124 |
125 | ///
126 | /// Size of the icons in pixels.
127 | ///
128 | public int IconSize
129 | {
130 | get => settings.IconSize;
131 | set
132 | {
133 | if (value != settings.IconSize)
134 | {
135 | settings.IconSize = value;
136 | OnPropertyChanged();
137 | }
138 | }
139 | }
140 |
141 | ///
142 | /// Theme of the dock.
143 | ///
144 | public ThemeModel Theme
145 | {
146 | get => settings.Theme;
147 | set
148 | {
149 | if (value != settings.Theme)
150 | {
151 | settings.Theme = value;
152 | OnPropertyChanged();
153 | }
154 | }
155 | }
156 |
157 | private ObservableCollection _icons;
158 |
159 | ///
160 | /// Icons of the dock.
161 | ///
162 | public ObservableCollection Icons
163 | {
164 | get
165 | {
166 | if (IsInDesignMode)
167 | _icons = new ObservableCollection
168 | {
169 | new(new IconModel { Name = "explorer.exe", Path = @"explorer.exe" }),
170 | new(new IconModel { Name = "notepad.exe", Path = @"notepad.exe" }),
171 | new(new IconModel { Name = "unknown.exe", Path = @"unknown.exe" })
172 | };
173 | if (_icons == null)
174 | _icons = new ObservableCollection(settings.Icons.Select(i => new IconViewModel(i)));
175 | return _icons;
176 | }
177 | }
178 |
179 | ///
180 | /// Save the settings automatically or when the dock is closed.
181 | ///
182 | public bool SaveAutomatically
183 | {
184 | get => settings.SaveAutomatically;
185 | set
186 | {
187 | if (value != settings.SaveAutomatically)
188 | {
189 | settings.SaveAutomatically = value;
190 | OnPropertyChanged();
191 | }
192 | }
193 | }
194 |
195 | ///
196 | /// Reserve the bottom of the screen for the dock.
197 | ///
198 | public bool ReserveScreenSpace
199 | {
200 | get => settings.ReserveScreenSpace;
201 | set
202 | {
203 | if (value != settings.ReserveScreenSpace)
204 | {
205 | settings.ReserveScreenSpace = value;
206 | OnPropertyChanged();
207 | }
208 |
209 | DockManager.Manager.SetWorkingArea(settings.ReserveScreenSpace);
210 | }
211 | }
212 |
213 | ///
214 | /// Remote windows taskbar to replace it with the dock.
215 | ///
216 | public bool RemoveTaskbar
217 | {
218 | get => settings.RemoveTaskbar;
219 | set
220 | {
221 | if (value != settings.RemoveTaskbar)
222 | {
223 | settings.RemoveTaskbar = value;
224 | OnPropertyChanged();
225 | }
226 |
227 | TaskbarManager.SetTaskbarVisibility(!value);
228 | }
229 | }
230 |
231 | ///
232 | /// Hide the dock automatically when it occludes an other window.
233 | ///
234 | public bool AutoHide
235 | {
236 | get => settings.AutoHide;
237 | set
238 | {
239 | if (value != settings.AutoHide)
240 | {
241 | settings.AutoHide = value;
242 | OnPropertyChanged();
243 | }
244 | }
245 | }
246 |
247 | ///
248 | /// Show the widgets in the corner of the screen.
249 | ///
250 | public bool ShowWidgets
251 | {
252 | get => settings.ShowWidgets;
253 | set
254 | {
255 | if (value != settings.ShowWidgets)
256 | {
257 | settings.ShowWidgets = value;
258 | OnPropertyChanged();
259 | }
260 | }
261 | }
262 |
263 | #endregion
264 | }
--------------------------------------------------------------------------------
/CleverDock/Views/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
32 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
65 |
66 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
78 |
80 |
81 |
82 |
83 |
91 |
94 |
97 |
100 |
101 |
103 |
105 |
107 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/CleverDock/Managers/WindowManager.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Windows;
4 | using System.Windows.Threading;
5 | using CleverDock.Handlers;
6 | using CleverDock.Interop;
7 | using CleverDock.Patterns;
8 | using CleverDock.Tools;
9 | using CleverDock.ViewModels;
10 |
11 | namespace CleverDock.Managers;
12 |
13 | public class WindowManager
14 | {
15 | private const long WS_OVERLAPPED = 0x00000000L;
16 | private const long WS_CHILD = 0x40000000L;
17 | private const long WS_POPUP = 0x80000000L;
18 | private const long WS_BORDER = 0x00800000L;
19 | private const long WS_VISIBLE = 0x10000000L;
20 | private const long TARGETWINDOW = WS_BORDER | WS_VISIBLE;
21 | private const long WS_EX_APPWINDOW = 0x00040000L;
22 | private const long WS_EX_TOOLWINDOW = 0x00000080L;
23 | private const long WS_EX_CLIENTEDGE = 0x00000200L;
24 | private const long WS_EX_WINDOWEDGE = 0x00000100L;
25 | private const long WS_EX_DLGMODALFRAME = 0x00000001L;
26 | private const long WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE;
27 |
28 | private static WindowManager manager;
29 | public IntPtr ActiveWindow;
30 | public Rect ActiveWindowRect;
31 |
32 | private Thread checkWindowsThread;
33 | private Thread checkWorkingAreaThread;
34 | private IntPtr dockHwnd;
35 |
36 | public WindowManager()
37 | {
38 | Windows = new List();
39 | WindowListChanged += WindowManager_WindowListChanged;
40 | ActiveWindowChanged += WindowManager_ActiveWindowChanged;
41 | WindowAdded += WindowManager_WindowAdded;
42 | WindowRemoved += WindowManager_WindowRemoved;
43 | Application.Current.Exit += Current_Exit;
44 | }
45 |
46 | public List Windows { get; set; }
47 |
48 | public static WindowManager Manager =>
49 | // Singleton Pattern
50 | manager ?? (manager = new WindowManager());
51 |
52 | public event EventHandler WorkingAreaChanged;
53 | public event EventHandler WindowListChanged;
54 | public event EventHandler WindowAdded;
55 | public event EventHandler WindowRemoved;
56 | public event EventHandler ActiveWindowRectChanged;
57 | public event EventHandler ActiveWindowChanged;
58 |
59 | private void Current_Exit(object sender, ExitEventArgs e)
60 | {
61 | Stop();
62 | }
63 |
64 | ~WindowManager()
65 | {
66 | Stop();
67 | }
68 |
69 | public void SetDockHwnd(IntPtr hwnd)
70 | {
71 | dockHwnd = hwnd;
72 | }
73 |
74 | public void Start()
75 | {
76 | Stop();
77 | checkWorkingAreaThread = new Thread(() =>
78 | {
79 | var rect = WorkAreaManager.GetWorkingArea();
80 | while (true)
81 | {
82 | var newRect = WorkAreaManager.GetWorkingArea();
83 | if (rect.Bottom != newRect.Bottom
84 | || rect.Top != newRect.Top
85 | || rect.Left != newRect.Left
86 | || rect.Right != newRect.Right)
87 | {
88 | if (WorkingAreaChanged != null)
89 | WorkingAreaChanged(this, new EventArgs());
90 | rect = newRect;
91 | }
92 |
93 | Thread.Sleep(100); // ~10ips
94 | }
95 | });
96 | checkWorkingAreaThread.Start();
97 | checkWindowsThread = new Thread(() =>
98 | {
99 | while (true)
100 | {
101 | // Check windows list
102 | var windowCount = 0;
103 | WindowInterop.EnumWindows((h, p) =>
104 | {
105 | if (isTaskBarWindow(h))
106 | windowCount++;
107 | return true;
108 | }, 0);
109 | if (windowCount != Windows.Count && WindowListChanged != null)
110 | WindowListChanged(this, new EventArgs());
111 | // Check active window
112 | var activeHwnd = WindowInterop.GetForegroundWindow();
113 | var activeWindow = new Win32Window(activeHwnd);
114 | var isDock = activeWindow.FileName == Process.GetCurrentProcess().MainModule.FileName;
115 | if (ActiveWindow != activeHwnd && ActiveWindowChanged != null && !isDock)
116 | ActiveWindowChanged(activeHwnd, new EventArgs());
117 | // Check active window location
118 | if (activeHwnd != IntPtr.Zero && !isDock)
119 | {
120 | var windowRect = new WindowInterop.Rect();
121 | WindowInterop.GetWindowRect(activeHwnd, ref windowRect);
122 | if (windowRect != ActiveWindowRect && ActiveWindowRectChanged != null)
123 | ActiveWindowRectChanged(this, new WindowRectEventArgs(ActiveWindowRect = windowRect));
124 | }
125 |
126 | Thread.Sleep(50); // ~20ips
127 | }
128 | });
129 | checkWindowsThread.Start();
130 | }
131 |
132 | public void Stop()
133 | {
134 | if (checkWorkingAreaThread != null)
135 | checkWorkingAreaThread.Interrupt();
136 | if (checkWindowsThread != null)
137 | checkWindowsThread.Interrupt();
138 | }
139 |
140 | private void WindowManager_ActiveWindowChanged(object sender, EventArgs e)
141 | {
142 | ActiveWindow = (IntPtr)sender;
143 | }
144 |
145 | private void WindowManager_WindowListChanged(object sender, EventArgs e)
146 | {
147 | var hwnds = new List();
148 | WindowInterop.EnumWindows((h, p) =>
149 | {
150 | if (isTaskBarWindow(h))
151 | hwnds.Add(h);
152 | return true;
153 | }, 0);
154 | var chwnds = (from w in Windows select w.Hwnd).ToList();
155 | foreach (var h in hwnds.Except(chwnds)) // Get removed windows
156 | {
157 | var window = new Win32Window(h);
158 | Windows.Add(window);
159 | if (WindowAdded != null)
160 | WindowAdded(this, new WindowEventArgs(window));
161 | }
162 |
163 | foreach (var h in chwnds.Except(hwnds)) // Get added windows
164 | {
165 | var window = Windows.Find(w => w.Hwnd == h);
166 | if (WindowRemoved != null)
167 | WindowRemoved(this, new WindowEventArgs(window));
168 | Windows.Remove(window);
169 | }
170 | }
171 |
172 | private void WindowManager_WindowRemoved(object sender, WindowEventArgs e)
173 | {
174 | Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
175 | {
176 | var foundItem = VMLocator.Main.Icons.FirstOrDefault(i => i.Windows.Any(w => w.Hwnd == e.Window.Hwnd));
177 | if (foundItem != null)
178 | {
179 | foundItem.Windows.Remove(e.Window);
180 | if (!foundItem.Windows.Any() && !foundItem.Pinned)
181 | VMLocator.Main.Icons.Remove(foundItem);
182 | }
183 | }));
184 | }
185 |
186 | private void WindowManager_WindowAdded(object sender, WindowEventArgs e)
187 | {
188 | Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
189 | {
190 | var foundItems = VMLocator.Main.Icons.Where(i => PathTools.SamePath(i.Path, e.Window.FileName));
191 | IconViewModel windowIcon = null;
192 | if (foundItems.Any())
193 | windowIcon = foundItems.First();
194 | if (!foundItems.Any() || (windowIcon != null && windowIcon.Windows.Count > 0))
195 | {
196 | windowIcon = new IconViewModel
197 | {
198 | Name = Path.GetFileName(e.Window.FileName),
199 | Path = e.Window.FileName,
200 | Pinned = false
201 | };
202 | VMLocator.Main.Icons.Add(windowIcon);
203 | }
204 |
205 | windowIcon.Windows.Add(e.Window);
206 | }));
207 | }
208 |
209 | private bool isTaskBarWindow(IntPtr hwnd)
210 | {
211 | //long style = WindowInterop.GetWindowLongPtr(hwnd, WindowInterop.GWL_STYLE); // Get style.
212 | if ( /*HasFlag(style, TARGETWINDOW) &&*/
213 | WindowInterop.IsWindowVisible(hwnd) /*&&
214 | WindowInterop.GetParent(hwnd) == IntPtr.Zero*/)
215 | {
216 | var window = new Win32Window(hwnd);
217 | var noOwner = WindowInterop.GetWindow(hwnd, WindowInterop.GW_OWNER) == IntPtr.Zero;
218 | var exStyle = WindowInterop.GetWindowLongPtr(hwnd, WindowInterop.GWL_EXSTYLE); // Get extended style.
219 | var isWin10App = window.ClassName == "ApplicationFrameWindow";
220 | return noOwner && (exStyle & WS_EX_TOOLWINDOW) == 0 && !isWin10App;
221 | }
222 |
223 | return false;
224 | }
225 |
226 | private long getHwndStyleFlags(IntPtr hwnd)
227 | {
228 | return WindowInterop.GetWindowLongPtr(hwnd, WindowInterop.GWL_STYLE);
229 | }
230 |
231 | private bool HasFlag(long value, long flag)
232 | {
233 | return (value & flag) == flag;
234 | }
235 | }
--------------------------------------------------------------------------------
/CleverDock/Themes/Metal2/style.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
35 |
47 |
48 |
60 |
72 |
85 |
117 |
134 |
178 |
182 |
200 |
--------------------------------------------------------------------------------
/CleverDock/Themes/Dark2/style.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
35 |
47 |
48 |
60 |
72 |
85 |
117 |
134 |
178 |
182 |
200 |
--------------------------------------------------------------------------------
/CleverDock/Themes/FlatLight/style.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
35 |
47 |
48 |
60 |
72 |
85 |
129 |
174 |
182 |
186 |
204 |
--------------------------------------------------------------------------------
/CleverDock/Themes/Flat/style.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
35 |
47 |
48 |
60 |
72 |
85 |
129 |
175 |
183 |
187 |
205 |
--------------------------------------------------------------------------------
/CleverDock/Models/ViewModels/IconViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Collections.Specialized;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Windows;
6 | using System.Windows.Input;
7 | using System.Windows.Media;
8 | using CleverDock.Interop;
9 | using CleverDock.Managers;
10 | using CleverDock.Models;
11 | using CleverDock.Models.ViewModels;
12 | using CleverDock.Patterns;
13 | using CleverDock.Tools;
14 |
15 | namespace CleverDock.ViewModels;
16 |
17 | public class IconViewModel : ViewModelBase
18 | {
19 | private const string ThemeFolder = "Themes";
20 | private readonly IconModel model = new();
21 |
22 | public IconViewModel()
23 | {
24 | WindowManager.Manager.ActiveWindowChanged += Manager_ActiveWindowChanged;
25 | Windows = new ObservableCollection();
26 | Windows.CollectionChanged += Windows_CollectionChanged;
27 | MinimizeCommand = new ActionCommand(MinimizeAction);
28 | RestoreCommand = new ActionCommand(RestoreAction);
29 | CloseCommand = new ActionCommand(CloseAction);
30 | ExitCommand = new ActionCommand(ExitAction);
31 | }
32 |
33 | public IconViewModel(IconModel model)
34 | : this()
35 | {
36 | Pinned = true;
37 | Path = model.Path;
38 | ImagePath = model.ImagePath;
39 | Name = model.Name;
40 | Text = StringUtils.LimitCharacters(model.Name, 40, 50);
41 | }
42 |
43 | #region Utilities
44 |
45 | ///
46 | /// FrameworkElement reference set by DockIconLoadedBehavior.
47 | ///
48 | public FrameworkElement Element { get; set; }
49 |
50 | #endregion
51 |
52 | private void Manager_ActiveWindowChanged(object sender, EventArgs e)
53 | {
54 | UpdateWindowState();
55 | }
56 |
57 | private void UpdateWindowState()
58 | {
59 | var window = Windows.FirstOrDefault();
60 | if (window == null)
61 | return;
62 | CanMinimize = !window.IsMinimized;
63 | CanRestore = window.IsMinimized;
64 | }
65 |
66 | #region Events
67 |
68 | public event EventHandler OnAnimateIconBounce;
69 |
70 | public void AnimateIconBounce()
71 | {
72 | if (OnAnimateIconBounce != null)
73 | OnAnimateIconBounce(this, null);
74 | }
75 |
76 | public event EventHandler OnMinimize;
77 |
78 | public void Minimize()
79 | {
80 | if (OnMinimize != null)
81 | OnMinimize(this, null);
82 | var window = Windows.FirstOrDefault();
83 | if (window != null)
84 | window.Minimize();
85 | }
86 |
87 | public event EventHandler OnRestore;
88 |
89 | public void Restore()
90 | {
91 | if (OnRestore != null)
92 | OnRestore(this, null);
93 | var window = Windows.FirstOrDefault();
94 | if (window != null)
95 | window.Restore();
96 | }
97 |
98 | public event EventHandler OnClose;
99 |
100 | public void Close()
101 | {
102 | if (OnClose != null)
103 | OnClose(this, null);
104 | var window = Windows.FirstOrDefault();
105 | if (window != null)
106 | window.Close();
107 | }
108 |
109 | public event EventHandler OnToggle;
110 |
111 | public void Toggle()
112 | {
113 | if (OnToggle != null)
114 | OnToggle(this, null);
115 | var window = Windows.FirstOrDefault();
116 | if (window != null)
117 | {
118 | if (window.IsActive)
119 | Minimize();
120 | else
121 | Restore();
122 | }
123 | }
124 |
125 | #endregion
126 |
127 | #region Methods
128 |
129 | public ActionCommand ExitCommand { get; private set; }
130 |
131 | private void ExitAction()
132 | {
133 | Application.Current.Shutdown();
134 | }
135 |
136 | public ActionCommand MinimizeCommand { get; private set; }
137 |
138 | private void MinimizeAction()
139 | {
140 | Minimize();
141 | }
142 |
143 | public ActionCommand RestoreCommand { get; private set; }
144 |
145 | private void RestoreAction()
146 | {
147 | Restore();
148 | }
149 |
150 | public ActionCommand CloseCommand { get; private set; }
151 |
152 | private void CloseAction()
153 | {
154 | Close();
155 | }
156 |
157 | public void Run()
158 | {
159 | if (Windows.Count > 0
160 | && !Keyboard.IsKeyDown(Key.LeftShift))
161 | {
162 | Toggle();
163 | }
164 | else if (!string.IsNullOrEmpty(Path) && File.Exists(Path))
165 | {
166 | Process.Start(Path);
167 | AnimateIconBounce();
168 | }
169 | }
170 |
171 | private void Windows_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
172 | {
173 | UpdateImages();
174 | UpdateWindowState();
175 | }
176 |
177 | private void UpdateImages()
178 | {
179 | var hasWindows = Windows.Any();
180 | IsActive = hasWindows;
181 | if (hasWindows)
182 | {
183 | var window = Windows.First();
184 | Text = string.IsNullOrEmpty(window.Title) ? System.IO.Path.GetFileName(window.FileName) : window.Title;
185 | Text = StringUtils.LimitCharacters(Text, 40, 50);
186 | var bitmap = IconManager.GetIcon(window.FileName, VMLocator.Main.IconSize) ?? IconManager.UnknownIcon;
187 | Icon = bitmap;
188 | BlurredIcon = BitmapEffectHelper.GaussianBlur(bitmap, 2.5f);
189 | ChildIcon = IconManager.GetAppIcon(window.Hwnd);
190 | HasChildIcon = WindowManager.Manager.Windows.Count(w => w.FileName == window.FileName) > 1;
191 | }
192 |
193 | if (Pinned && string.IsNullOrEmpty(ImagePath))
194 | {
195 | var bitmap = IconManager.GetIcon(Path, VMLocator.Main.IconSize) ?? IconManager.UnknownIcon;
196 | Icon = bitmap;
197 | BlurredIcon = BitmapEffectHelper.GaussianBlur(bitmap, 2.5f);
198 | }
199 | }
200 |
201 | #endregion
202 |
203 | #region Binded Properties
204 |
205 | private ImageSource _icon;
206 |
207 | ///
208 | /// Image of the icon.
209 | ///
210 | public ImageSource Icon
211 | {
212 | get => _icon;
213 | set
214 | {
215 | if (value != _icon)
216 | {
217 | _icon = value;
218 | OnPropertyChanged();
219 | }
220 | }
221 | }
222 |
223 | private ImageSource _blurredIcon;
224 |
225 | ///
226 | /// Preblurred image of the icon.
227 | /// TODO: Blur the icon in Icon's setter.
228 | ///
229 | public ImageSource BlurredIcon
230 | {
231 | get => _blurredIcon;
232 | set
233 | {
234 | if (value != _blurredIcon)
235 | {
236 | _blurredIcon = value;
237 | OnPropertyChanged();
238 | }
239 | }
240 | }
241 |
242 | private ImageSource _childIcon;
243 |
244 | ///
245 | /// Child icon.
246 | ///
247 | public ImageSource ChildIcon
248 | {
249 | get => _childIcon;
250 | set
251 | {
252 | if (value != _childIcon)
253 | {
254 | _childIcon = value;
255 | OnPropertyChanged();
256 | }
257 | }
258 | }
259 |
260 | private string _text;
261 |
262 | ///
263 | /// Title of the icon.
264 | ///
265 | public string Text
266 | {
267 | get => _text;
268 | set
269 | {
270 | if (value != _text)
271 | {
272 | _text = value;
273 | OnPropertyChanged();
274 | }
275 | }
276 | }
277 |
278 | private bool _isActive;
279 |
280 | ///
281 | /// True if the window is focused.
282 | ///
283 | public bool IsActive
284 | {
285 | get => _isActive;
286 | set
287 | {
288 | if (value != _isActive)
289 | {
290 | _isActive = value;
291 | OnPropertyChanged();
292 | }
293 | }
294 | }
295 |
296 | private bool _canMinimize;
297 |
298 | ///
299 | /// True if the window can be minimized
300 | ///
301 | public bool CanMinimize
302 | {
303 | get => _canMinimize;
304 | set
305 | {
306 | if (value != _canMinimize)
307 | {
308 | _canMinimize = value;
309 | OnPropertyChanged();
310 | }
311 | }
312 | }
313 |
314 | private bool _canRestore;
315 |
316 | ///
317 | /// True if the window can be restored
318 | ///
319 | public bool CanRestore
320 | {
321 | get => _canRestore;
322 | set
323 | {
324 | if (value != _canRestore)
325 | {
326 | _canRestore = value;
327 | OnPropertyChanged();
328 | }
329 | }
330 | }
331 |
332 | private bool _hasChildIcon;
333 |
334 | ///
335 | /// True if the child icon should be displayed
336 | ///
337 | public bool HasChildIcon
338 | {
339 | get => _hasChildIcon;
340 | set
341 | {
342 | if (value != _hasChildIcon)
343 | {
344 | _hasChildIcon = value;
345 | OnPropertyChanged();
346 | }
347 | }
348 | }
349 |
350 | private ObservableCollection _windows;
351 |
352 | ///
353 | /// Windows attached to this icon.
354 | ///
355 | public ObservableCollection Windows
356 | {
357 | get => _windows;
358 | set
359 | {
360 | if (value != _windows)
361 | {
362 | _windows = value;
363 | OnPropertyChanged();
364 | }
365 | }
366 | }
367 |
368 |
369 | private ObservableCollection _themes;
370 |
371 | ///
372 | /// Available themes.
373 | ///
374 | public ObservableCollection Themes
375 | {
376 | get
377 | {
378 | if (_themes == null)
379 | _themes = new ObservableCollection
380 | (new ThemeViewModel[] { new(ThemeManager.DefaultTheme) }
381 | .Concat(Directory.GetFiles(ThemeFolder, "theme.json", SearchOption.AllDirectories)
382 | .Select(f => new ThemeViewModel(new ThemeModel(f)))));
383 | return _themes;
384 | }
385 | }
386 |
387 | ///
388 | /// The path to the icon's executable or file.
389 | ///
390 | public string Path
391 | {
392 | get => model.Path;
393 | set
394 | {
395 | if (value != model.Path)
396 | {
397 | model.Path = value;
398 | UpdateImages();
399 | OnPropertyChanged();
400 | }
401 | }
402 | }
403 |
404 | ///
405 | /// Optional. The path to the image used for the icon, if set to null the system icon will be used.
406 | ///
407 | public string ImagePath
408 | {
409 | get => model.ImagePath;
410 | set
411 | {
412 | if (value != model.ImagePath)
413 | {
414 | model.ImagePath = value;
415 | UpdateImages();
416 | OnPropertyChanged();
417 | }
418 | }
419 | }
420 |
421 | ///
422 | /// The name of the icon.
423 | ///
424 | public string Name
425 | {
426 | get => model.Name;
427 | set
428 | {
429 | if (value != model.Name)
430 | {
431 | model.Name = value;
432 | OnPropertyChanged();
433 | }
434 | }
435 | }
436 |
437 | private bool _pinned { get; set; }
438 |
439 | ///
440 | /// When the icon is pinned, it stays in the dock.
441 | ///
442 | public bool Pinned
443 | {
444 | get => _pinned;
445 | set
446 | {
447 | if (value != _pinned)
448 | {
449 | _pinned = value;
450 | OnPropertyChanged();
451 | }
452 | }
453 | }
454 |
455 | #endregion
456 | }
--------------------------------------------------------------------------------
/CleverDock/Interop/IconInterop.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace CleverDock.Interop;
5 |
6 | internal class IconInterop
7 | {
8 | ///
9 | /// Retrieve the handle to the icon that represents the file and the index of the icon within the system image list.
10 | /// The handle is copied to the hIcon member of the structure specified by psfi, and the index is copied to the iIcon
11 | /// member.
12 | ///
13 | public const int SHGFI_ICON = 0x100;
14 |
15 | ///
16 | /// Modify SHGFI_ICON, causing the function to retrieve the file's large icon (32x32). The SHGFI_ICON flag must also be
17 | /// set.
18 | ///
19 | public const int SHGFI_LARGEICON = 0x0;
20 |
21 | ///
22 | /// Modify SHGFI_ICON, causing the function to retrieve the file's small icon (16x16). The SHGFI_ICON flag must also be
23 | /// set.
24 | ///
25 | public const int SHGFI_SMALLICON = 0x1;
26 |
27 | ///
28 | /// Used by SHGetImageList. These images are the Shell standard extra-large icon size. This is typically 48x48, but the
29 | /// size can be customized by the user.
30 | ///
31 | public const int SHIL_EXTRALARGE = 0x2;
32 |
33 | ///
34 | /// Used by SHGetImageList. Windows Vista and later. The image is normally 256x256 pixels.
35 | ///
36 | public const int SHIL_JUMBO = 0x4;
37 |
38 | public const int WM_CLOSE = 0x0010;
39 |
40 | ///
41 | /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the
42 | /// specified window and does not return until the window procedure has processed the message.
43 | ///
44 | /// A handle to the window whose window procedure will receive the message.
45 | /// The message to be sent.
46 | /// Additional message-specific information.
47 | /// Additional message-specific information.
48 | /// The return value specifies the result of the message processing; it depends on the message sent.
49 | [DllImport("user32")]
50 | public static extern
51 | IntPtr SendMessage(
52 | IntPtr handle,
53 | int Msg,
54 | IntPtr wParam,
55 | IntPtr lParam
56 | );
57 |
58 | /// SHGetImageList is not exported correctly in XP. See KB316931
59 | /// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q316931
60 | /// Apparently (and hopefully) ordinal 727 isn't going to change.
61 | ///
62 | /// Retrieves an image list.
63 | ///
64 | /// The image type contained in the list.
65 | /// Reference to the image list interface identifier, normally IID_IImageList.
66 | ///
67 | /// When this method returns, contains the interface pointer requested in riid. This is typically
68 | /// IImageList.
69 | ///
70 | /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
71 | [DllImport("shell32.dll", EntryPoint = "#727")]
72 | public static extern int SHGetImageList(
73 | int iImageList,
74 | ref Guid riid,
75 | out IImageList ppv
76 | );
77 |
78 | ///
79 | /// Retrieves information about an object in the file system, such as a file, folder, directory, or drive root.
80 | ///
81 | ///
82 | /// A pointer to a null-terminated string of maximum length MAX_PATH that contains the path and file
83 | /// name. Both absolute and relative paths are valid.
84 | ///
85 | ///
86 | /// A combination of one or more file attribute flags (FILE_ATTRIBUTE_ values as defined in
87 | /// Winnt.h). If uFlags does not include the SHGFI_USEFILEATTRIBUTES flag, this parameter is ignored.
88 | ///
89 | /// Pointer to a SHFILEINFO structure to receive the file information.
90 | /// The size, in bytes, of the SHFILEINFO structure pointed to by the psfi parameter.
91 | /// The flags that specify the file information to retrieve.
92 | /// Returns a value whose meaning depends on the uFlags parameter.
93 | [DllImport("Shell32.dll")]
94 | public static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo,
95 | uint uFlags);
96 |
97 | ///
98 | /// Destroys an icon and frees any memory the icon occupied.
99 | ///
100 | /// A handle to the icon to be destroyed. The icon must not be in use.
101 | /// If the function succeeds, the return value is nonzero.
102 | [DllImport("user32")]
103 | public static extern int DestroyIcon(IntPtr hIcon);
104 |
105 | #region Nested type: IMAGEINFO
106 |
107 | ///
108 | /// Contains information about an image in an image list. This structure is used with the IImageList::GetImageInfo
109 | /// function.
110 | ///
111 | [StructLayout(LayoutKind.Sequential)]
112 | public struct IMAGEINFO
113 | {
114 | ///
115 | /// A handle to the bitmap that contains the images.
116 | ///
117 | private readonly IntPtr hbmImage;
118 |
119 | ///
120 | /// A handle to a monochrome bitmap that contains the masks for the images. If the image list does not contain a mask,
121 | /// this member is NULL.
122 | ///
123 | private readonly IntPtr hbmMask;
124 |
125 | ///
126 | /// Not used. This member should always be zero.
127 | ///
128 | private readonly int Unused1;
129 |
130 | ///
131 | /// Not used. This member should always be zero.
132 | ///
133 | private readonly int Unused2;
134 |
135 | ///
136 | /// The bounding rectangle of the specified image within the bitmap specified by hbmImage.
137 | ///
138 | private readonly RECT rcImage;
139 | }
140 |
141 | #endregion
142 |
143 | #region Private ImageList COM Interop (XP)
144 |
145 | ///
146 | /// Exposes methods that manipulate and interact with image lists.
147 | ///
148 | [ComImport]
149 | [Guid("46EB5926-582E-4017-9FDF-E8998DAA0950")]
150 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
151 | public interface IImageList
152 | {
153 | [PreserveSig]
154 | int Add(
155 | IntPtr hbmImage,
156 | IntPtr hbmMask,
157 | ref int pi);
158 |
159 | [PreserveSig]
160 | int ReplaceIcon(
161 | int i,
162 | IntPtr hicon,
163 | ref int pi);
164 |
165 | [PreserveSig]
166 | int SetOverlayImage(
167 | int iImage,
168 | int iOverlay);
169 |
170 | [PreserveSig]
171 | int Replace(
172 | int i,
173 | IntPtr hbmImage,
174 | IntPtr hbmMask);
175 |
176 | [PreserveSig]
177 | int AddMasked(
178 | IntPtr hbmImage,
179 | int crMask,
180 | ref int pi);
181 |
182 | [PreserveSig]
183 | int Draw(
184 | ref IMAGELISTDRAWPARAMS pimldp);
185 |
186 | [PreserveSig]
187 | int Remove(
188 | int i);
189 |
190 | [PreserveSig]
191 | int GetIcon(
192 | int i,
193 | int flags,
194 | ref IntPtr picon);
195 |
196 | [PreserveSig]
197 | int GetImageInfo(
198 | int i,
199 | ref IMAGEINFO pImageInfo);
200 |
201 | [PreserveSig]
202 | int Copy(
203 | int iDst,
204 | IImageList punkSrc,
205 | int iSrc,
206 | int uFlags);
207 |
208 | [PreserveSig]
209 | int Merge(
210 | int i1,
211 | IImageList punk2,
212 | int i2,
213 | int dx,
214 | int dy,
215 | ref Guid riid,
216 | ref IntPtr ppv);
217 |
218 | [PreserveSig]
219 | int Clone(
220 | ref Guid riid,
221 | ref IntPtr ppv);
222 |
223 | [PreserveSig]
224 | int GetImageRect(
225 | int i,
226 | ref RECT prc);
227 |
228 | [PreserveSig]
229 | int GetIconSize(
230 | ref int cx,
231 | ref int cy);
232 |
233 | [PreserveSig]
234 | int SetIconSize(
235 | int cx,
236 | int cy);
237 |
238 | [PreserveSig]
239 | int GetImageCount(
240 | ref int pi);
241 |
242 | [PreserveSig]
243 | int SetImageCount(
244 | int uNewCount);
245 |
246 | [PreserveSig]
247 | int SetBkColor(
248 | int clrBk,
249 | ref int pclr);
250 |
251 | [PreserveSig]
252 | int GetBkColor(
253 | ref int pclr);
254 |
255 | [PreserveSig]
256 | int BeginDrag(
257 | int iTrack,
258 | int dxHotspot,
259 | int dyHotspot);
260 |
261 | [PreserveSig]
262 | int EndDrag();
263 |
264 | [PreserveSig]
265 | int DragEnter(
266 | IntPtr hwndLock,
267 | int x,
268 | int y);
269 |
270 | [PreserveSig]
271 | int DragLeave(
272 | IntPtr hwndLock);
273 |
274 | [PreserveSig]
275 | int DragMove(
276 | int x,
277 | int y);
278 |
279 | [PreserveSig]
280 | int SetDragCursorImage(
281 | ref IImageList punk,
282 | int iDrag,
283 | int dxHotspot,
284 | int dyHotspot);
285 |
286 | [PreserveSig]
287 | int DragShowNolock(
288 | int fShow);
289 |
290 | [PreserveSig]
291 | int GetDragImage(
292 | ref POINT ppt,
293 | ref POINT pptHotspot,
294 | ref Guid riid,
295 | ref IntPtr ppv);
296 |
297 | [PreserveSig]
298 | int GetItemFlags(
299 | int i,
300 | ref int dwFlags);
301 |
302 | [PreserveSig]
303 | int GetOverlayImage(
304 | int iOverlay,
305 | ref int piIndex);
306 | }
307 |
308 | #endregion
309 |
310 | #region Nested type: IMAGELISTDRAWPARAMS
311 |
312 | ///
313 | /// Contains information about an image list draw operation and is used with the IImageList::Draw function.
314 | ///
315 | public struct IMAGELISTDRAWPARAMS
316 | {
317 | ///
318 | /// Used with the alpha blending effect.
319 | /// When used with ILS_ALPHA, this member holds the value for the alpha channel. This value can be from 0 to 255, with
320 | /// 0 being completely transparent, and 255 being completely opaque.
321 | /// You must use comctl32.dll version 6 to use this member. See the Remarks.
322 | ///
323 | public int Frame;
324 |
325 | ///
326 | /// The size of this structure, in bytes.
327 | ///
328 | public int cbSize;
329 |
330 | ///
331 | /// A color used for the glow and shadow effects. You must use comctl32.dll version 6 to use this member. See the
332 | /// Remarks.
333 | ///
334 | public int crEffect;
335 |
336 | ///
337 | /// A value that specifies the number of pixels to draw, relative to the upper-left corner of the drawing operation as
338 | /// specified by xBitmap and yBitmap. If cx and cy are zero, then Draw draws the entire valid section. The method does
339 | /// not ensure that the parameters are valid.
340 | ///
341 | public int cx;
342 |
343 | ///
344 | /// A value that specifies the number of pixels to draw, relative to the upper-left corner of the drawing operation as
345 | /// specified by xBitmap and yBitmap. If cx and cy are zero, then Draw draws the entire valid section. The method does
346 | /// not ensure that the parameters are valid.
347 | ///
348 | public int cy;
349 |
350 | ///
351 | /// A value specifying a raster operation code. These codes define how the color data for the source rectangle will be
352 | /// combined with the color data for the destination rectangle to achieve the final color. This member is ignored if
353 | /// fStyle does not include the ILD_ROP flag.
354 | ///
355 | public int dwRop;
356 |
357 | ///
358 | /// A flag that specifies the drawing state. This member can contain one or more image list state flags. You must use
359 | /// comctl32.dll version 6 to use this member.
360 | ///
361 | public int fState;
362 |
363 | ///
364 | /// A flag specifying the drawing style and, optionally, the overlay image. See the comments section at the end of this
365 | /// topic for information on the overlay image.
366 | ///
367 | public int fStyle;
368 |
369 | ///
370 | /// A handle to the destination device context.
371 | ///
372 | public IntPtr hdcDst;
373 |
374 | ///
375 | /// A handle to the image list that contains the image to be drawn.
376 | ///
377 | public IntPtr himl;
378 |
379 | ///
380 | /// The zero-based index of the image to be drawn.
381 | ///
382 | public int i;
383 |
384 | ///
385 | /// The image background color.
386 | ///
387 | public int rgbBk;
388 |
389 | ///
390 | /// The image foreground color. This member is used only if fStyle includes the ILD_BLEND25 or ILD_BLEND50 flag.
391 | ///
392 | public int rgbFg;
393 |
394 | ///
395 | /// The x-coordinate that specifies where the image is drawn.
396 | ///
397 | public int x;
398 |
399 | ///
400 | /// The x-coordinate that specifies the upper-left corner of the drawing operation in reference to the image itself.
401 | /// Pixels of the image that are to the left of xBitmap and above yBitmap do not appear.
402 | ///
403 | public int xBitmap;
404 |
405 | ///
406 | /// The y-coordinate that specifies where the image is drawn.
407 | ///
408 | public int y;
409 |
410 | ///
411 | /// The y-coordinate that specifies the upper-left corner of the drawing operation in reference to the image itself.
412 | /// Pixels of the image that are to the left of xBitmap and above yBitmap do not appear.
413 | ///
414 | public int yBitmap;
415 | }
416 |
417 | #endregion
418 |
419 | #region Nested type: POINT
420 |
421 | [StructLayout(LayoutKind.Sequential)]
422 | public struct POINT
423 | {
424 | public int X;
425 | public int Y;
426 |
427 | public POINT(int x, int y)
428 | {
429 | X = x;
430 | Y = y;
431 | }
432 |
433 | public static implicit operator Point(POINT p)
434 | {
435 | return new Point(p.X, p.Y);
436 | }
437 |
438 | public static implicit operator POINT(Point p)
439 | {
440 | return new POINT(p.X, p.Y);
441 | }
442 | }
443 |
444 | #endregion
445 |
446 | #region Nested type: RECT
447 |
448 | [StructLayout(LayoutKind.Sequential)]
449 | public struct RECT
450 | {
451 | private readonly int _Left;
452 | private readonly int _Top;
453 | private readonly int _Right;
454 | private readonly int _Bottom;
455 | }
456 |
457 | #endregion
458 |
459 | #region Nested type: SHFILEINFO
460 |
461 | ///
462 | /// Contains information about a file object.
463 | ///
464 | [StructLayout(LayoutKind.Sequential)]
465 | public struct SHFILEINFO
466 | {
467 | ///
468 | /// A handle to the icon that represents the file.
469 | ///
470 | public IntPtr hIcon;
471 |
472 | ///
473 | /// The index of the icon image within the system image list.
474 | ///
475 | public int iIcon;
476 |
477 | ///
478 | /// An array of values that indicates the attributes of the file object.
479 | ///
480 | public uint dwAttributes;
481 |
482 | ///
483 | /// A string that contains the name of the file as it appears in the Windows Shell, or the path and file name of the
484 | /// file that contains the icon representing the file.
485 | ///
486 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
487 | public string szDisplayName;
488 |
489 | ///
490 | /// A string that describes the type of file.
491 | ///
492 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
493 | public string szTypeName;
494 | }
495 |
496 | #endregion
497 | }
--------------------------------------------------------------------------------