├── .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 | 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 | ![alt tag](https://raw.githubusercontent.com/ldom66/clever-dock/master/screenshot-0.2.0.jpg) 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 | 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 | } --------------------------------------------------------------------------------