diff --git a/src/BetterControls/BetterMenu/BetterContextMenu.cs b/src/BetterControls/BetterMenu/BetterContextMenu.cs new file mode 100644 index 0000000..2a723c1 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterContextMenu.cs @@ -0,0 +1,45 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System.ComponentModel; +using System.Drawing; + +namespace BetterControls +{ + /// + /// Represents a context menu. + /// + [ToolboxItem(true)] + [DesignTimeVisible(true)] + [ToolboxBitmap(typeof(BetterToolbar), "BetterContextMenuIcon.bmp")] + public class BetterContextMenu : BetterMenuRoot + { + /// + /// Initialize a new instance of . + /// + public BetterContextMenu() { } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenu.Events.cs b/src/BetterControls/BetterMenu/BetterMenu.Events.cs new file mode 100644 index 0000000..e40688b --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenu.Events.cs @@ -0,0 +1,93 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Menu classes. + /// + partial class BetterMenu + { + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnAutoSizeItemsChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + AutoSizeItemsChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnCustomStatusWidthChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + CustomStatusWidthChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnDefaultButtonChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + DefaultButtonChanged?.Invoke(this, e); + } + + /// + /// This event is raised has been changed. + /// + public event EventHandler AutoSizeItemsChanged; + + /// + /// This event is raised has been changed. + /// + public event EventHandler CustomStatusWidthChanged; + + /// + /// This event is raised has been changed. + /// + public event EventHandler DefaultButtonChanged; + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenu.Initialization.cs b/src/BetterControls/BetterMenu/BetterMenu.Initialization.cs new file mode 100644 index 0000000..cd21c34 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenu.Initialization.cs @@ -0,0 +1,91 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Menu classes. + /// + partial class BetterMenu + { + /// + /// + /// + protected override void CreateHandle() + { + if (!IsHandleCreated) + _handle = UnsafeNativeMethods.CreatePopupMenu(); + + base.CreateHandle(); + } + + /// + /// + /// + protected override void DestroyHandle() + { + if (IsHandleCreated) + { + UnsafeNativeMethods.DestroyMenu(GetHandleRef()); + _handle = IntPtr.Zero; + + foreach (BetterMenuItem item in Items) + item.DestroyHandle(); + } + + base.DestroyHandle(); + } + + /// + /// + /// + /// + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + + InitializeItems(); + } + + /// + /// Initializes items in the menu. + /// + private void InitializeItems() + { + foreach (BetterMenuItem item in Items) + { + if (!item.Visible) + continue; + + NativeMethods.MENUITEMINFO_T structure = item.ComputeMenuItemInfoT(); + + UnsafeNativeMethods.InsertMenuItem(GetHandleRef(), -1, true, structure); + } + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenu.cs b/src/BetterControls/BetterMenu/BetterMenu.cs new file mode 100644 index 0000000..f5e30b0 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenu.cs @@ -0,0 +1,294 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.ComponentModel; +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Menu classes. + /// + [ToolboxItem(false)] + [DesignTimeVisible(false)] + [Designer("BetterMenuDesigner")] + public abstract partial class BetterMenu : BetterMenuBase + { + /// + /// Initialize a new instance of . + /// + private protected BetterMenu() + { + MenuReferences.Add(this, MenuReference); + + _items = new BetterMenuItemCollection(this); + _menuReference = new BetterMenuReference(this); + } + + /// + /// Initialize a new instance of . + /// + /// The owner menu as an instance of . + private protected BetterMenu(BetterMenu ownerMenu) + : base(ownerMenu) + { } + + private bool _autoSizeItems = true; + private int _customStatusWidth; + private BetterMenuButton _defaultButton; + private BetterMenuItemCollection _items; + private BetterMenuReference _menuReference; + + /// + /// Gets or sets a value indicating whether or not the width of the status column of items should be automatically computed. + /// + [Category(Categories.Behavior)] + [Description("Value indicating whether or not the width of the status column of items should be automatically computed.")] + [DefaultValue(true)] + [Localizable(false)] + public virtual bool AutoSizeItems + { + get => _autoSizeItems; + set + { + if (AutoSizeItems != value) + { + _autoSizeItems = value; + + OnAutoSizeItemsChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the width of the status column of items. This value will be ignored unless is set to . + /// + [Category(Categories.Behavior)] + [Description("The width of the status column of items.")] + [DefaultValue(0)] + [Localizable(false)] + public virtual int CustomStatusWidth + { + get => _customStatusWidth; + set + { + if (CustomStatusWidth != value) + { + _customStatusWidth = value; + + OnCustomStatusWidthChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the default button in the menu items of this menu item. This item will usually appear as bold. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public BetterMenuButton DefaultButton + { + get => _defaultButton; + set + { + if (DefaultButton != value) + { + _defaultButton = value; + + if (DefaultButton != null) + { + UnsafeNativeMethods.SetMenuDefaultItem(GetHandleRef(), DefaultButton.UniqueIdentifier, false); + } + else + { + UnsafeNativeMethods.SetMenuDefaultItem(GetHandleRef(), -1, false); + } + + OnDefaultButtonChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets the focused push button in the menu items of this menu item. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public BetterMenuButton FocusedPushbutton => _focusedButton; + + /// + /// Gets the collection of menu items for this menu. + /// + [Category(Categories.Behavior)] + [Description("The collection of menu items for this menu.")] + [DefaultValue(null)] + [Localizable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public virtual BetterMenuItemCollection Items => _items; + + /// + /// Gets the menu reference for this menu as an instance of . + /// + internal BetterMenuReference MenuReference => _menuReference; + + /// + /// Gets the root menu as an instance of . + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual BetterMenuRoot MenuRoot + { + get + { + if (OwnerMenu != null) + return OwnerMenu.MenuRoot; + + return null; + } + } + + /// + /// Gets the unique identifier of this menu item, relative to the the entire process scope. + /// + [Browsable(false)] + public int UniqueIdentifier + { + get + { + if (MenuReference != null) + return MenuReference.UniqueIdentifier; + + return -1; + } + } + + + + + + + + + + + + + + + + #region Private Member Fields + + public IntPtr _handle; + internal BetterMenuButton _focusedButton; + + #endregion + + #region Properties + + /// + /// + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override IntPtr Handle + { + get + { + IntPtr handle = base.Handle; + + + + return _handle; + } + } + + /// + /// + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override bool IsHandleCreated => _handle != IntPtr.Zero; + + /// + /// Gets an exhaustive collection of menu items directly or indirectly under the root menu. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + static internal BetterMenuReferenceCollection MenuReferences { get; } = new BetterMenuReferenceCollection(); + + #endregion + + #region Miscellaneous + + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void Dispose(bool disposing) + { + if (disposing && Items != null) + { + int index = Items.Count - 1; + + while (index >= 0) + { + BetterMenu menu = Items[index--]; + + if (menu.Site != null && menu.Site.Container != null) + { + menu.Site.Container.Remove(menu); + } + + //menu.OwnerControl2 = null; + menu.Dispose(); + } + + _items = null; + } + if (IsHandleCreated) + { + UnsafeNativeMethods.DestroyMenu(new HandleRef(this, Handle)); + + _handle = IntPtr.Zero; + } + + base.Dispose(disposing); + } + + + + + + + + #endregion + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuBase.cs b/src/BetterControls/BetterMenu/BetterMenuBase.cs new file mode 100644 index 0000000..4a75e29 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuBase.cs @@ -0,0 +1,78 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Menu classes. + /// + public abstract class BetterMenuBase : HandledComponentCollectionElement + { + /// + /// Initialize a new instance of . + /// + private protected BetterMenuBase() { } + + /// + /// Initialize a new instance of . + /// + /// The owner menu as an instance of . + private protected BetterMenuBase(BetterMenu ownerMenu) + : base(ownerMenu) + { } + + /// + /// Gets the owner menu as an instance of . + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public BetterMenu OwnerMenu + { + get + { + if (OwnerElement != null) + return (BetterMenu)OwnerElement; + + return null; + } + } + + /// + /// Gets a for the current component. + /// + /// An instance of . + protected internal virtual HandleRef GetHandleRef() + { + if (!IsHandleCreated) + return default; + + return new HandleRef(this, Handle); + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuButtonClickEventArgs.cs b/src/BetterControls/BetterMenu/BetterMenuButtonClickEventArgs.cs new file mode 100644 index 0000000..08e271e --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuButtonClickEventArgs.cs @@ -0,0 +1,55 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; + +namespace BetterControls +{ + /// + /// Event arguments for when a direct or indirect descendant menu button of a menu root is clicked. + /// + public class BetterMenuButtonClickEventArgs : EventArgs + { + /// + /// Initialize a new instance of . + /// + /// The menu item that was clicked. + public BetterMenuButtonClickEventArgs(BetterMenuButton menuItem) + { + MenuItem = menuItem ?? throw new ArgumentNullException(nameof(menuItem)); + } + + /// + /// Gets the menu item that was clicked. + /// + public BetterMenuButton MenuItem { get; } + + /// + /// Gets a value indicating whether or not the menu item that was clicked is a direct descendant of the menu root. + /// + public bool DirectDescendant => MenuItem.OwnerMenu == MenuItem.MenuRoot; + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuButtonFocusedEventArgs.cs b/src/BetterControls/BetterMenu/BetterMenuButtonFocusedEventArgs.cs new file mode 100644 index 0000000..ccf1231 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuButtonFocusedEventArgs.cs @@ -0,0 +1,55 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; + +namespace BetterControls +{ + /// + /// Event arguments for when a direct or indirect descendant menu button of a menu root is focused. + /// + public class BetterMenuButtonFocusedEventArgs : EventArgs + { + /// + /// Initialize a new instance of . + /// + /// The menu item that was focused. + public BetterMenuButtonFocusedEventArgs(BetterMenuButton menuItem) + { + MenuItem = menuItem ?? throw new ArgumentNullException(nameof(menuItem)); + } + + /// + /// Gets the menu item that was focused. + /// + public BetterMenuButton MenuItem { get; } + + /// + /// Gets a value indicating whether or not the menu item that was focused is a direct descendant of the menu root. + /// + public bool DirectDescendant => MenuItem.OwnerMenu == MenuItem.MenuRoot; + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuButtonImageAlign.cs b/src/BetterControls/BetterMenu/BetterMenuButtonImageAlign.cs new file mode 100644 index 0000000..1280728 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuButtonImageAlign.cs @@ -0,0 +1,71 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +namespace BetterControls +{ + /// + /// The different alignments for images within menu items. + /// + public enum BetterMenuButtonImageAlign + { + /// + /// Top-left alignment. + /// + TopLeft = 0, + /// + /// Top-middle alignment. + /// + TopMiddle = 1, + /// + /// Top-right alignment. + /// + TopRight = 2, + /// + /// Middle-left alignment. + /// + MiddleLeft = 3, + /// + /// Middle-middle alignment. + /// + MiddleMiddle = 4, + /// + /// Middle-right alignment. + /// + MiddleRight = 5, + /// + /// Bottom-left alignment. + /// + BottomLeft = 6, + /// + /// Bottom-middle alignment. + /// + BottomMiddle = 7, + /// + /// Bottom-right alignment. + /// + BottomRight = 8 + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuButtonImageIndexer.cs b/src/BetterControls/BetterMenu/BetterMenuButtonImageIndexer.cs new file mode 100644 index 0000000..f92879f --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuButtonImageIndexer.cs @@ -0,0 +1,71 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.Drawing; +using System; +using System.ComponentModel; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Used to provide a mechanism for a menu button to support using both image indexes and image keys. + /// + public class BetterMenuButtonImageIndexer : ImageIndexer + { + /// + /// Initialize a new instance of . + /// + /// The parent toolbar item as an instance of . + public BetterMenuButtonImageIndexer(BetterMenuButton ownerMenuButton) + { + _ownerMenuButton = ownerMenuButton ?? throw new ArgumentNullException(nameof(ownerMenuButton)); + } + + private BetterMenuButton _ownerMenuButton; + + /// + /// Gets the owner menu item as an instance of . + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public BetterMenuButton OwnerMenuButton => _ownerMenuButton; + + /// + /// + /// + protected override ImageList ImageList + { + get + { + if (OwnerMenuButton != null) + return OwnerMenuButton.MenuRoot.ImageList; + + return null; + } + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuExtensions.cs b/src/BetterControls/BetterMenu/BetterMenuExtensions.cs new file mode 100644 index 0000000..ba035cc --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuExtensions.cs @@ -0,0 +1,50 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; + +namespace BetterControls +{ + /// + /// Extension methods for menus. + /// + public static class BetterMenuExtensions + { + /// + /// Adds a to the collection of menu items. + /// + /// The collection of menu items to add to. + public static void AddSeparator(this BetterMenuItemCollection items) + { + if (items is null) + { + throw new ArgumentNullException(nameof(items)); + } + + items.Add(new BetterMenuSeparator()); + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuHighlightBehavior.cs b/src/BetterControls/BetterMenu/BetterMenuHighlightBehavior.cs new file mode 100644 index 0000000..e2b8ea2 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuHighlightBehavior.cs @@ -0,0 +1,21 @@ +namespace BetterControls +{ + /// + /// Highlight behavior for when a menu is shown. + /// + public enum BetterMenuHighlightBehavior + { + /// + /// Doesn't change the highlight behavior. Menu items with set to will continue to highlight. + /// + Default = 0, + /// + /// Highlights the first item in addition to any other items configured to highlight. Menu items with set to will continue to highlight. + /// + HighlightFirstItem = 1, + /// + /// Highlights the first item only. Menu items with set to will not continue to highlight. + /// + HighlightFirstItemExclusive = 2 + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuItemChangeType.cs b/src/BetterControls/BetterMenu/BetterMenuItemChangeType.cs new file mode 100644 index 0000000..e9f6314 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuItemChangeType.cs @@ -0,0 +1,11 @@ +namespace BetterControls +{ + /// + /// The different ways a menu item can be changed. + /// + public enum BetterMenuItemChangeType + { + SoftChange = 0, + HardChange = 1 + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuItemCheckStyle.cs b/src/BetterControls/BetterMenu/BetterMenuItemCheckStyle.cs new file mode 100644 index 0000000..1c9271d --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuItemCheckStyle.cs @@ -0,0 +1,47 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +namespace BetterControls +{ + /// + /// The different check styles for a better menu item. + /// + public enum BetterMenuItemCheckStyle + { + /// + /// The item is not checked. + /// + Disabled = 0, + /// + /// The item is checked with a check mark. + /// + Check = 1, + /// + /// The item is checked with a radio style check mark. + /// + Radio = 2 + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuItemCollection.cs b/src/BetterControls/BetterMenu/BetterMenuItemCollection.cs new file mode 100644 index 0000000..e70861f --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuItemCollection.cs @@ -0,0 +1,372 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.Collections; +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Design; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Represents a collection of . + /// + [Editor("BetterMenuItemCollectionEditor", typeof(UITypeEditor))] + public class BetterMenuItemCollection : ElementCollection, IEnumerable + { + /// + /// Initialize a new instance of . + /// + /// The owner component as an instance of . + internal BetterMenuItemCollection(BetterMenu ownerMenu) + : base(ownerMenu) + { } + + /// + /// Gets the owner menu as an instance of . + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + protected BetterMenu OwnerMenu + { + get + { + if (OwnerElement != null) + return (BetterMenu)OwnerElement; + + return null; + } + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + public void Add(string text) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Add(new BetterMenuPushButton() + { + Text = text + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The description of the menu item. + public void Add(string text, string description) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Description = description + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + public void Add(string text, int imageIndex) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + ImageIndex = imageIndex + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The description of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + public void Add(string text, string description, int imageIndex) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Description = description, + ImageIndex = imageIndex + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The image to be shown in the menu item. + public void Add(string text, Image image) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Image = image + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The description of the menu item. + /// The image to be shown in the menu item. + public void Add(string text, string description, Image image) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Description = description, + Image = image + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The shortcut key combination associated with the menu item. + public void Add(string text, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Shortcut = shortcut + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The description of the menu item. + /// The shortcut key combination associated with the menu item. + public void Add(string text, string description, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Description = description, + Shortcut = shortcut + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public void Add(string text, int imageIndex, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + ImageIndex = imageIndex, + Shortcut = shortcut + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The description of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public void Add(string text, string description, int imageIndex, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Description = description, + ImageIndex = imageIndex, + Shortcut = shortcut + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The image to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public void Add(string text, Image image, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Image = image, + Shortcut = shortcut + }); + } + + /// + /// Adds a new instance of to the collection. + /// + /// The text of the menu item. + /// The description of the menu item. + /// The image to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public void Add(string text, string description, Image image, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Add(new BetterMenuPushButton() + { + Text = text, + Description = description, + Image = image, + Shortcut = shortcut + }); + } + + + + + + /// + /// + /// + /// + /// + public override bool Remove(BetterMenuItem item) + { + if (item is null) + { + throw new ArgumentNullException(nameof(item)); + } + + item.MenuReference.Dispose(); + BetterMenu.MenuReferences.Remove(item); + + return base.Remove(item); + } + + /// + /// + /// + /// + public override object Clone() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuItemPaintStatusEventArgs.cs b/src/BetterControls/BetterMenu/BetterMenuItemPaintStatusEventArgs.cs new file mode 100644 index 0000000..eb6f7b5 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuItemPaintStatusEventArgs.cs @@ -0,0 +1,63 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.Drawing; + +namespace BetterControls +{ + /// + /// Event arguments for when the status part of each menu item is painted. + /// + public class BetterMenuItemPaintStatusEventArgs + { + /// + /// Initialize a new instance of . + /// + /// The graphics associated with this paint operation. + /// The outer bounds of this paint operation. + internal BetterMenuItemPaintStatusEventArgs(Graphics graphics, Rectangle bounds) + { + Graphics = graphics ?? throw new ArgumentNullException(nameof(graphics)); + Bounds = bounds; + } + + /// + /// Gets the device context associated with this paint operation. + /// + public IDeviceContext DeviceContext => Graphics; + + /// + /// Gets the graphics associated with this paint operation. + /// + public Graphics Graphics { get; } + + /// + /// Gets the outer bounds of this paint operation. + /// + public Rectangle Bounds { get; } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuReference.cs b/src/BetterControls/BetterMenu/BetterMenuReference.cs new file mode 100644 index 0000000..e3559db --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuReference.cs @@ -0,0 +1,79 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Used to create a low level reference of a specified menu. + /// + internal class BetterMenuReference : ICommandExecutor, IDisposable + { + /// + /// Initialize a new instance of . + /// + /// The menu associated with this menu reference. + public BetterMenuReference(BetterMenu menu) + { + _menu = menu ?? throw new ArgumentNullException(nameof(menu)); + + _globalCommand = new GlobalCommand(this); + } + + public BetterMenu _menu; + private GlobalCommand _globalCommand; + + /// + /// Gets the associated with this menu reference. + /// + public BetterMenu Menu => _menu; + + /// + /// Gets the unique identifier of this menu item, relative to the the entire process scope. + /// + public int UniqueIdentifier => _globalCommand.Identifier; + + /// + /// + /// + void ICommandExecutor.Execute() + { + if (_menu is BetterMenuButton button) + button.PerformClick(); + } + + /// + /// + /// + public void Dispose() + { + if (_globalCommand != null) + _globalCommand.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuReferenceCollection.cs b/src/BetterControls/BetterMenu/BetterMenuReferenceCollection.cs new file mode 100644 index 0000000..2476434 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuReferenceCollection.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace BetterControls +{ + internal class BetterMenuReferenceCollection : Dictionary + { + } +} diff --git a/src/BetterControls/BetterMenu/BetterMenuRoot.Events.cs b/src/BetterControls/BetterMenu/BetterMenuRoot.Events.cs new file mode 100644 index 0000000..81e4cf0 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuRoot.Events.cs @@ -0,0 +1,112 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; + +namespace BetterControls +{ + /// + /// Extend this class to create a root menu. + /// + partial class BetterMenuRoot + { + /// + /// This method is raised after the menu has collapsed. + /// + /// The event arguments as an instance of . + protected virtual void OnCollapse(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + Collapse?.Invoke(this, e); + } + + /// + /// This method is raised when a direct or indirect descendant menu button is clicked. + /// + /// The event arguments as an instance of . + protected virtual void OnMenuItemClick(BetterMenuButtonClickEventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + MenuItemClick?.Invoke(this, e); + } + + /// + /// This method is raised when a direct or indirect descendant menu button is focused. + /// + /// The event arguments as an instance of . + protected virtual void OnMenuItemFocused(BetterMenuButtonFocusedEventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + MenuItemFocused?.Invoke(this, e); + } + + /// + /// This method is raised before the menu is shown. + /// + /// The event arguments as an instance of . + protected virtual void OnPopup(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + Popup?.Invoke(this, e); + } + + /// + /// This event is raised after the menu has collapsed. + /// + public event EventHandler Collapse; + + /// + /// This event is raised when a direct or indirect descendant menu button is clicked. + /// + public event EventHandler MenuItemClick; + + /// + /// This event is raised when a direct or indirect descendant menu button is focused. + /// + public event EventHandler MenuItemFocused; + + /// + /// This event is raised before the menu is shown. + /// + public event EventHandler Popup; + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuRoot.cs b/src/BetterControls/BetterMenu/BetterMenuRoot.cs new file mode 100644 index 0000000..e6bebdb --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuRoot.cs @@ -0,0 +1,255 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.ComponentModel; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Extend this class to create a root menu. + /// + public abstract partial class BetterMenuRoot : BetterMenu + { + /// + /// Initialize a new instance of . + /// + public BetterMenuRoot() { } + + private ImageList _imageList; + private BetterMenuHighlightBehavior _showHighlightBehavior = BetterMenuHighlightBehavior.Default; + private Control _sourceControl; + private BetterMenuWndProcSubclass _wndProcSubclass; + + /// + /// Gets the image list for the menu root. + /// + [Category(Categories.Appearance)] + [Description("The image list for the menu root.")] + [DefaultValue(null)] + [Localizable(false)] + public virtual ImageList ImageList + { + get => _imageList; + set + { + if (ImageList != value) + { + _imageList = value; + } + } + } + + /// + /// + /// + public override BetterMenuRoot MenuRoot => this; + + /// + /// Gets or sets a value from indicating the highlight behavior when this root menu is shown. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal virtual BetterMenuHighlightBehavior ShowHighlightBehavior + { + get => _showHighlightBehavior; + set + { + if (ShowHighlightBehavior != value) + { + _showHighlightBehavior = value; + } + } + } + + /// + /// Gets the control that the menu is opened against. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public Control SourceControl => _sourceControl; + + /// + /// This method is raised when a direct or indirect descendant menu button is clicked. + /// + /// The menu item that was clicked. + [EditorBrowsable(EditorBrowsableState.Never)] + public void DescendantClicked(BetterMenuButton menuItem) + { + if (menuItem is null) + { + throw new ArgumentNullException(nameof(menuItem)); + } + + OnMenuItemClick(new BetterMenuButtonClickEventArgs(menuItem)); + } + + /// + /// This method is raised when a direct or indirect descendant menu button is focused. + /// + /// The menu item that was focused. + [EditorBrowsable(EditorBrowsableState.Never)] + public void DescendantFocused(BetterMenuButton menuItem) + { + if (menuItem is null) + { + throw new ArgumentNullException(nameof(menuItem)); + } + + OnMenuItemFocused(new BetterMenuButtonFocusedEventArgs(menuItem)); + } + + /// + /// Displays the menu at the specified position. + /// + /// The control that owns the menu. + /// The position relative to the screen that the menu should be shown from. + public virtual void Show(Control control, Point position) + { + if (control is null) + { + throw new ArgumentNullException(nameof(control)); + } + + int flags = NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON; + + Show(control, position, flags, BetterMenuHighlightBehavior.Default); + } + + /// + /// Displays the menu at the specified position. + /// + /// The control that owns the menu. + /// The position relative to the screen that the menu should be shown from. + /// The direction relative to the position that the menu should be shown from. + public virtual void Show(Control control, Point position, LeftRightAlignment alignment) + { + if (control is null) + { + throw new ArgumentNullException(nameof(control)); + } + + int flags = NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON; + + if (alignment == LeftRightAlignment.Left) + { + flags |= NativeMethods.TPM_RIGHTALIGN; + } + else + { + flags |= NativeMethods.TPM_LEFTALIGN; + } + + Show(control, position, flags, BetterMenuHighlightBehavior.Default); + } + + /// + /// Displays the menu at the specified position. + /// + /// The control that owns the menu. + /// The position relative to the screen that the menu should be shown from. + /// Value from indicating the highlight behavior when this root menu is shown. + public virtual void Show(Control control, Point position, BetterMenuHighlightBehavior highlightBehavior) + { + if (control is null) + { + throw new ArgumentNullException(nameof(control)); + } + + int flags = NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON; + + Show(control, position, flags, highlightBehavior); + } + + /// + /// Displays the menu at the specified position. + /// + /// The control that owns the menu. + /// The position relative to the screen that the menu should be shown from. + /// The direction relative to the position that the menu should be shown from. + /// Value from indicating the highlight behavior when this root menu is shown. + public virtual void Show(Control control, Point position, LeftRightAlignment alignment, BetterMenuHighlightBehavior highlightBehavior) + { + if (control is null) + { + throw new ArgumentNullException(nameof(control)); + } + + int flags = NativeMethods.TPM_VERTICAL | NativeMethods.TPM_RIGHTBUTTON; + + if (alignment == LeftRightAlignment.Left) + { + flags |= NativeMethods.TPM_RIGHTALIGN; + } + else + { + flags |= NativeMethods.TPM_LEFTALIGN; + } + + Show(control, position, flags, highlightBehavior); + } + + /// + /// Displays the menu at the specified position. + /// + /// The control that owns the menu. + /// The position relative to the screen that the menu should be shown from. + /// The native flags used to control the behavior of the menu. + /// Value from indicating the highlight behavior when this root menu is shown. + private protected virtual void Show(Control control, Point position, int flags, BetterMenuHighlightBehavior highlightBehavior) + { + if (control is null) + { + throw new ArgumentNullException(nameof(control)); + } + + _sourceControl = control; + _wndProcSubclass = new BetterMenuWndProcSubclass(SourceControl); + + position = control.PointToScreen(position); + + ShowHighlightBehavior = highlightBehavior; + RecreateHandle(); + + OnPopup(new EventArgs()); + + SafeNativeMethods.TrackPopupMenuEx(GetHandleRef(), flags, position.X, position.Y, new HandleRef(control, control.Handle), null); + + ShowHighlightBehavior = BetterMenuHighlightBehavior.Default; + + OnCollapse(new EventArgs()); + + _wndProcSubclass.ReleaseHandle(); + _wndProcSubclass = null; + _sourceControl = null; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuWndProcSubclass.MessageLoop.cs b/src/BetterControls/BetterMenu/BetterMenuWndProcSubclass.MessageLoop.cs new file mode 100644 index 0000000..6a9e9f7 --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuWndProcSubclass.MessageLoop.cs @@ -0,0 +1,87 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Used to intercept the message loop of the source control of a menu when opened. + /// + partial class BetterMenuWndProcSubclass + { + /// + /// + /// + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_MENUSELECT: + if (WmMenuSelect(ref m)) + return; + + break; + } + + base.WndProc(ref m); + } + + /// + /// Handles the WM_MENUSELECT message. + /// + private protected virtual bool WmMenuSelect(ref Message m) + { + // If the menu item is a popup item (i.e. it has sub-items) then the data sent + // with WM_MENUSELECT to identify which menu was selected differs than any other + // menu item. An index to the popup item is sent instead of command identifier. + int identifier = NativeMethods.Util.LOWORD(m.WParam); + int flags = NativeMethods.Util.HIWORD(m.WParam); + + if ((flags & NativeMethods.MF_POPUP) != 0) + { + // The item that was selected has sub-items; therefore, we have to identify + // it recursively using the internal menu tree. + BetterMenuItem item = GetMenuItemByIndex(m.LParam, identifier); + + if (item is BetterMenuButton button) + button.ItemFocused(); + } + else + { + // The item that was selected does not have any sub-items; therefore, we can + // identify it using its command identifier. + GlobalCommand globalCommand = GlobalCommand.GetCommandExecutor(identifier); + + if (globalCommand != null && globalCommand.CommandExecutor is BetterMenuReference menuReference && menuReference.Menu is BetterMenuButton button) + button.ItemFocused(); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/BetterMenuWndProcSubclass.cs b/src/BetterControls/BetterMenu/BetterMenuWndProcSubclass.cs new file mode 100644 index 0000000..023f27d --- /dev/null +++ b/src/BetterControls/BetterMenu/BetterMenuWndProcSubclass.cs @@ -0,0 +1,90 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Used to intercept the message loop of the source control of a menu when opened. + /// + internal partial class BetterMenuWndProcSubclass : NativeWindow + { + /// + /// Initialize a new instance of . + /// + /// The control whose window procedure should be subclassed. + public BetterMenuWndProcSubclass(Control control) + { + if (control is null) + { + throw new ArgumentNullException(nameof(control)); + } + + AssignHandle(control.Handle); + } + + /// + /// Gets a drop-down menu item by its index. + /// + /// The handle of the menu. + /// The index of the item in the menu to get. + /// The menu item as an instance of . + private BetterMenuItem GetMenuItemByIndex(IntPtr handle, int index) + { + int identifier = UnsafeNativeMethods.GetMenuItemID(new HandleRef(null, handle), index); + + if (identifier == -1) + { + // This menu item has sub-items. We have to recurse through the menu tree until we find an + // item that does not have any sub-items, and then iterate back up its menu tree based on its + // stored parent information. + IntPtr childHandle = UnsafeNativeMethods.GetSubMenu(new HandleRef(null, handle), index); + int childMenuCount = UnsafeNativeMethods.GetMenuItemCount(new HandleRef(null, childHandle)); + + for (int i = 0; i < childMenuCount;) + { + BetterMenuItem item = GetMenuItemByIndex(childHandle, i++); + + if (item != null && item.OwnerElement != null && item.OwnerElement is BetterMenuItem parentItem) + return parentItem; + } + } + else + { + // This menu item does not have any sub-items. + GlobalCommand globalCommand = GlobalCommand.GetCommandExecutor(identifier); + + if (globalCommand != null && globalCommand.CommandExecutor is BetterMenuReference menuReference && menuReference.Menu is BetterMenuItem item) + return item; + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuButton.Events.cs b/src/BetterControls/BetterMenu/Items/BetterMenuButton.Events.cs new file mode 100644 index 0000000..509e678 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuButton.Events.cs @@ -0,0 +1,527 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace BetterControls +{ + /// + /// Extend this class to create a menu item that is a button. + /// + partial class BetterMenuButton + { + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnAutoSizeChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + AutoSizeChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnCheckedChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + CheckedChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnCheckStyleChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + CheckStyleChanged?.Invoke(this, e); + } + + /// + /// This method is raised when the menu item is clicked. + /// + /// The event arguments as an instance of . + protected virtual void OnClick(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + Click?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnCustomHeightChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + CustomHeightChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnDescriptionChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + DescriptionChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnEnabledChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + EnabledChanged?.Invoke(this, e); + } + + /// + /// this method is raised when the menu item is focused. + /// + /// The event arguments as an instance of . + protected virtual void OnFocused(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + Focused?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnHighlightChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + HighlightChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnImageChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ImageChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnImageAlignChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ImageAlignChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnImageIndexChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ImageIndexChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnImageKeyChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ImageKeyChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnOwnerDrawChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + OwnerDrawChanged?.Invoke(this, e); + } + + /// + /// This method is raised when the status part of this item is painted. + /// + /// The event arguments as an instance of . + protected virtual void OnPaintStatus(BetterMenuItemPaintStatusEventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + if (OwnerMenu is null) + return; + + Image image = Image; + + if (image is null && MenuRoot.ImageList != null && ImageIndexer.ComputedIndex != -1) + image = MenuRoot.ImageList.Images[ImageIndexer.ComputedIndex]; + + if (UseShieldImage) + image = IconUtilities.GetShieldImage(); + + if (image != null) + { + int croppedWidth = image.Width; + int croppedHeight = image.Height; + + if (croppedWidth > e.Bounds.Width) + croppedWidth = e.Bounds.Width; + if (croppedHeight > e.Bounds.Height) + croppedHeight = e.Bounds.Height; + + Bitmap croppedImage = new Bitmap(croppedWidth, croppedHeight); + + using (Graphics graphics = Graphics.FromImage(croppedImage)) + graphics.DrawImage(image, -0, -0); + + int x = 0; + int y = 0; + + switch (ImageAlign) + { + case BetterMenuButtonImageAlign.TopMiddle: + x = (e.Bounds.Width - croppedWidth) / 2; + break; + case BetterMenuButtonImageAlign.TopRight: + x = e.Bounds.Width - croppedWidth; + break; + case BetterMenuButtonImageAlign.MiddleLeft: + y = (e.Bounds.Height - croppedHeight) / 2; + break; + case BetterMenuButtonImageAlign.MiddleMiddle: + x = (e.Bounds.Width - croppedWidth) / 2; + y = (e.Bounds.Height - croppedHeight) / 2; + break; + case BetterMenuButtonImageAlign.MiddleRight: + x = e.Bounds.Width - croppedWidth; + y = (e.Bounds.Height - croppedHeight) / 2; + break; + case BetterMenuButtonImageAlign.BottomLeft: + y = e.Bounds.Height - croppedHeight; + break; + case BetterMenuButtonImageAlign.BottomMiddle: + x = (e.Bounds.Width - croppedWidth) / 2; + y = e.Bounds.Height - croppedHeight; + break; + case BetterMenuButtonImageAlign.BottomRight: + x = e.Bounds.Width - croppedWidth; + y = e.Bounds.Height - croppedHeight; + break; + } + + e.Graphics.DrawImage(croppedImage, x, y, croppedWidth, croppedHeight); + } + + OnPaintCheck(e); + + PaintStatus?.Invoke(this, e); + } + + /// + /// This method is raised when the check image of this item is painted. + /// + /// The event arguments as an instance of . + protected virtual void OnPaintCheck(BetterMenuItemPaintStatusEventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + if (CheckStyle == BetterMenuItemCheckStyle.Disabled) + return; + + Bitmap checkBitmap = (Bitmap)Helper.VisualStyleRendererToImage(null, new Rectangle(0, 0, 16, 16), CheckStyle == BetterMenuItemCheckStyle.Check); + + int x = 0; + int y = 0; + + switch (CheckAlign) + { + case BetterMenuButtonImageAlign.TopMiddle: + x = (e.Bounds.Width - checkBitmap.Width) / 2; + break; + case BetterMenuButtonImageAlign.TopRight: + x = e.Bounds.Width - checkBitmap.Width; + break; + case BetterMenuButtonImageAlign.MiddleLeft: + y = (e.Bounds.Height - checkBitmap.Height) / 2; + break; + case BetterMenuButtonImageAlign.MiddleMiddle: + x = (e.Bounds.Width - checkBitmap.Width) / 2; + y = (e.Bounds.Height - checkBitmap.Height) / 2; + break; + case BetterMenuButtonImageAlign.MiddleRight: + x = e.Bounds.Width - checkBitmap.Width; + y = (e.Bounds.Height - checkBitmap.Height) / 2; + break; + case BetterMenuButtonImageAlign.BottomLeft: + y = e.Bounds.Height - checkBitmap.Height; + break; + case BetterMenuButtonImageAlign.BottomMiddle: + x = (e.Bounds.Width - checkBitmap.Width) / 2; + y = e.Bounds.Height - checkBitmap.Height; + break; + case BetterMenuButtonImageAlign.BottomRight: + x = e.Bounds.Width - checkBitmap.Width; + y = e.Bounds.Height - checkBitmap.Height; + break; + } + + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + e.Graphics.DrawImage(checkBitmap, x, y, checkBitmap.Width, checkBitmap.Height); + + PaintCheck?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnShortcutChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ShortcutChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnShowShortcutChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ShowShortcutChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnTextChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + TextChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnUseShieldImageChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + UseShieldImageChanged?.Invoke(this, e); + } + + /// + /// This event is raised when has been changed. + /// + public event EventHandler AutoSizeChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler CheckedChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler CheckStyleChanged; + + /// + /// This event is raised when the menu item is clicked. + /// + public event EventHandler Click; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler CustomHeightChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler DescriptionChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler EnabledChanged; + + /// + /// This event is raised when the menu item is focused. + /// + public event EventHandler Focused; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler HighlightChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler ImageChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler ImageAlignChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler ImageIndexChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler ImageKeyChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler OwnerDrawChanged; + + /// + /// This event is raised when the status part of this item is painted. + /// + public event EventHandler PaintStatus; + + /// + /// This event is raised when the status part of this item is painted. + /// + public event EventHandler PaintCheck; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler ShortcutChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler ShowShortcutChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler TextChanged; + + /// + /// This event is raised when has been changed. + /// + public event EventHandler UseShieldImageChanged; + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuButton.NativeStructures.cs b/src/BetterControls/BetterMenu/Items/BetterMenuButton.NativeStructures.cs new file mode 100644 index 0000000..d9a89c4 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuButton.NativeStructures.cs @@ -0,0 +1,257 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.VisualStyles; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; + +namespace BetterControls +{ + /// + /// Extend this class to create a menu item that is a button. + /// + partial class BetterMenuButton + { + /// + /// + /// + /// + internal override NativeMethods.MENUITEMINFO_T ComputeMenuItemInfoT() + { + NativeMethods.MENUITEMINFO_T structure = base.ComputeMenuItemInfoT(); + + if (!Enabled) + structure.fState |= NativeMethods.MFS_DISABLED; + if (Checked) + structure.fState |= NativeMethods.MFS_CHECKED; + if (CheckStyle == BetterMenuItemCheckStyle.Radio) + structure.fType |= NativeMethods.MFT_RADIOCHECK; + if (OwnerDraw) + structure.fType |= NativeMethods.MFT_OWNERDRAW; + + if (OwnerMenu == MenuRoot && MenuRoot.ShowHighlightBehavior == BetterMenuHighlightBehavior.HighlightFirstItem && DisplayedItemIndex == 0) + { + structure.fState |= NativeMethods.MFS_HILITE; + } + else if (OwnerMenu == MenuRoot && MenuRoot.ShowHighlightBehavior == BetterMenuHighlightBehavior.HighlightFirstItemExclusive) + { + if (DisplayedItemIndex == 0) + structure.fState |= NativeMethods.MFS_HILITE; + } + else if (Highlight) + { + structure.fState |= NativeMethods.MFS_HILITE; + } + + structure.fMask |= NativeMethods.MIIM_BITMAP | NativeMethods.MIIM_STRING; + + // The image in the native structure is also used as a mechanism to set the height of each item + // and the width of the status area. + using (Bitmap bitmap = CreateStatusImage()) + structure.hbmpItem = bitmap.GetHbitmap(Color.FromArgb(0, 0, 0, 0)); + + string text = Text; + + if (string.IsNullOrEmpty(text)) + text = " "; + + structure.dwTypeData = text; + + // Setup the text for the menu item. If a shortkey key is specified and showing shortcuts is enabled, then the shortcut + // string literal is appended to the end of the text. Item text should not be empty, so this is checked. + if (ShowShortcut && Shortcut != Shortcut.None && Items.IsEmpty) + structure.dwTypeData += "\t" + TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString((Keys)(int)Shortcut); + + return structure; + } + + + + /// + /// Creates the status image for this item. + /// + /// The created image as an instance of . + private protected virtual Bitmap CreateStatusImage() + { + if (OwnerMenu is null) + return null; + + int statusColumnWidth = 16; + int itemHeight = 16; + + if (!OwnerMenu.AutoSizeItems && OwnerMenu.CustomStatusWidth != 0) + statusColumnWidth = OwnerMenu.CustomStatusWidth; + + if (!AutoSize && CustomHeight != 0) + itemHeight = CustomHeight; + + int computedWidth = statusColumnWidth; + + if (!Application.RenderWithVisualStyles) + computedWidth += 5; + + Bitmap bitmap = new Bitmap(computedWidth, itemHeight, PixelFormat.Format32bppPArgb); + + using (Graphics graphics = Graphics.FromImage(bitmap)) + OnPaintStatus(new BetterMenuItemPaintStatusEventArgs(graphics, new Rectangle(0, 0, statusColumnWidth, itemHeight))); + + bitmap.MakeTransparent(); + + return bitmap; + } + + + + + public class Helper + { + #region Win32 Native APIs + internal class NativeMethods + { + // CreateDIBSection funcation iUsage value + internal const int DIB_RGB_COLORS = 0x00; + internal const int DIB_PAL_COLORS = 0x01; + internal const int DIB_PAL_INDICES = 0x02; + + [DllImport("gdi32.dll", CharSet = CharSet.Unicode)] + internal static extern bool DeleteObject(IntPtr hObject); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr GetDC(IntPtr hwnd); + + [DllImport("gdi32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr CreateCompatibleDC(IntPtr hdc); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc); + + [DllImport("gdi32.dll", CharSet = CharSet.Unicode)] + internal static extern int DeleteDC(IntPtr hdc); + + [DllImport("gdi32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); + + [DllImport("gdi32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint iUsage, + out IntPtr bits, IntPtr hSection, uint dwOffset); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "RtlMoveMemory")] + internal static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); + + [StructLayout(LayoutKind.Sequential)] + internal struct BITMAPINFO + { + public Int32 biSize; + public Int32 biWidth; + public Int32 biHeight; + public Int16 biPlanes; + public Int16 biBitCount; + public Int32 biCompression; + public Int32 biSizeImage; + public Int32 biXPelsPerMeter; + public Int32 biYPelsPerMeter; + public Int32 biClrUsed; + public Int32 biClrImportant; + } + } + #endregion + + public static Image VisualStyleRendererToImage(VisualStyleRenderer renderer, Rectangle bounds, bool check) + { + if (ToolStripManager.VisualStylesEnabled) + { + using (Bitmap bit = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb)) + { + NativeMethods.BITMAPINFO bmi = new NativeMethods.BITMAPINFO(); + + bmi.biWidth = bit.Width; + bmi.biHeight = -bit.Height; + bmi.biPlanes = 1; + bmi.biBitCount = 32; + bmi.biXPelsPerMeter = (int)bit.HorizontalResolution; + bmi.biYPelsPerMeter = (int)bit.VerticalResolution; + bmi.biSize = Marshal.SizeOf(typeof(NativeMethods.BITMAPINFO)); + + IntPtr bits; + IntPtr bmp = NativeMethods.CreateDIBSection(IntPtr.Zero, ref bmi, + NativeMethods.DIB_RGB_COLORS, out bits, IntPtr.Zero, 0); + + IntPtr dc = NativeMethods.GetDC(IntPtr.Zero); + IntPtr hdc = NativeMethods.CreateCompatibleDC(dc); + NativeMethods.SelectObject(hdc, bmp); + + using (Graphics g = Graphics.FromHdc(hdc)) + { + if (check) + { + MenuItemCheckRenderer.DrawCheck(g, new Point(0, 0), true); + } + else + { + MenuItemRadioRenderer.DrawRadio(g, new Point(0, 0), true); + } + } + + Bitmap image = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppPArgb); + + using (Bitmap tempImage = new Bitmap(bounds.Width, bounds.Height, bounds.Width * 4, + PixelFormat.Format32bppPArgb, bits)) + { + BitmapData tempBitmapData = tempImage.LockBits(bounds, ImageLockMode.ReadOnly, + PixelFormat.Format32bppPArgb); + BitmapData bitmapData = image.LockBits(bounds, ImageLockMode.WriteOnly, + PixelFormat.Format32bppPArgb); + + NativeMethods.CopyMemory(bitmapData.Scan0, tempBitmapData.Scan0, + (uint)tempBitmapData.Stride * (uint)tempBitmapData.Height); + + tempImage.UnlockBits(tempBitmapData); + image.UnlockBits(bitmapData); + } + + NativeMethods.DeleteObject(bmp); + NativeMethods.DeleteDC(hdc); + NativeMethods.ReleaseDC(IntPtr.Zero, dc); + + return image; + } + } + else + { + return new Bitmap(bounds.Width, bounds.Height); + } + } + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuButton.cs b/src/BetterControls/BetterMenu/Items/BetterMenuButton.cs new file mode 100644 index 0000000..82e6767 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuButton.cs @@ -0,0 +1,763 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.ComponentModel; +using BetterControls.Drawing; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Design; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Extend this class to create a menu item that is a button. + /// + public abstract partial class BetterMenuButton : BetterMenuItem + { + /// + /// Initialize a new instance of . + /// + public BetterMenuButton() + { + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + public BetterMenuButton(string text) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + public BetterMenuButton(string text, string description) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Text = text; + Description = description; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + public BetterMenuButton(string text, int imageIndex) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text; + ImageIndex = imageIndex; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + public BetterMenuButton(string text, string description, int imageIndex) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Text = text; + Description = description; + ImageIndex = imageIndex; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The image to be shown in the menu item. + public BetterMenuButton(string text, Image image) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text; + Image = image; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The image to be shown in the menu item. + public BetterMenuButton(string text, string description, Image image) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Text = text; + Description = description; + Image = image; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuButton(string text, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text; + Shortcut = shortcut; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuButton(string text, string description, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Text = text; + Description = description; + Shortcut = shortcut; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuButton(string text, int imageIndex, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text; + ImageIndex = imageIndex; + Shortcut = shortcut; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuButton(string text, string description, int imageIndex, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Text = text; + Description = description; + ImageIndex = imageIndex; + Shortcut = shortcut; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The image to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuButton(string text, Image image, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text; + Image = image; + Shortcut = shortcut; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The image to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuButton(string text, string description, Image image, Shortcut shortcut) + { + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + Text = text; + Description = description; + Image = image; + Shortcut = shortcut; + + _imageIndexer = new BetterMenuButtonImageIndexer(this); + } + + /// + /// Initialize a new instance of . + /// + /// The owner menu as an instance of . + private protected BetterMenuButton(BetterMenu ownerMenu) + : base(ownerMenu) + { } + + private bool _autoSize = true; + private bool _checked = false; + private BetterMenuButtonImageAlign _checkAlign = BetterMenuButtonImageAlign.MiddleRight; + private BetterMenuItemCheckStyle _checkStyle = BetterMenuItemCheckStyle.Disabled; + private int _customHeight = 0; + private string _description = string.Empty; + private bool _enabled = true; + private bool _highlight = false; + private Image _image; + private BetterMenuButtonImageAlign _imageAlign = BetterMenuButtonImageAlign.MiddleRight; + private BetterMenuButtonImageIndexer _imageIndexer; + private bool _ownerDraw = false; + private Shortcut _shortcut = Shortcut.None; + private bool _showShortcut = true; + private string _text = string.Empty; + private bool _useShieldImage = false; + + /// + /// Gets or sets a value indicating whether or not the menu item should be auto-sized. + /// + [Category(Categories.Behavior)] + [Description("Value indicating whether or not the menu item should be auto-sized.")] + [DefaultValue(true)] + [Localizable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public virtual bool AutoSize + { + get => _autoSize; + set + { + if (AutoSize != value) + { + _autoSize = value; + + PerformItemChanged(CollectionElementItemChangedFlags.None); + + OnAutoSizeChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value indicating whether or not the menu item is checked. + /// + [Category(Categories.Appearance)] + [Description("Value indicating whether or not the menu item is checked.")] + [DefaultValue(false)] + [Localizable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + public virtual bool Checked + { + get => _checked; + set + { + if (Checked != value) + { + if (value is true && Items.Count != 0) + return; + + _checked = value; + + if (CheckStyle == BetterMenuItemCheckStyle.Disabled && Checked == true) + { + CheckStyle = BetterMenuItemCheckStyle.Check; + } + else if (CheckStyle != BetterMenuItemCheckStyle.Disabled && Checked == false) + { + CheckStyle = BetterMenuItemCheckStyle.Disabled; + } + + OnCheckedChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value from indicating the alignment of the check when specified. + /// + [Category(Categories.Appearance)] + [Description("Value indicating the alignment of the check when specified.")] + [DefaultValue(typeof(BetterMenuButtonImageAlign), "MiddleRight")] + [Localizable(false)] + public virtual BetterMenuButtonImageAlign CheckAlign + { + get => _checkAlign; + set + { + if (CheckAlign != value) + { + _checkAlign = value; + } + } + } + + /// + /// Gets or sets a value from indicating the check style of the item when checked. + /// + [Localizable(true)] + [DefaultValue(typeof(BetterMenuItemCheckStyle), "Disabled")] + [Category(Categories.Appearance)] + [Description("Value indicating the check style of the item when checked.")] + public virtual BetterMenuItemCheckStyle CheckStyle + { + get => _checkStyle; + set + { + if (CheckStyle != value) + { + _checkStyle = value; + + if (CheckStyle == BetterMenuItemCheckStyle.Check || CheckStyle == BetterMenuItemCheckStyle.Radio) + { + _checked = true; + } + else + { + _checked = false; + } + + OnCheckStyleChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value indicating the item height. + /// + [Category(Categories.Appearance)] + [Description("Value indicating the item height.")] + [DefaultValue(0)] + [Localizable(false)] + public virtual int CustomHeight + { + get => _customHeight; + set + { + if (_customHeight != value) + { + if (value < 0 || value > 256) + throw new ArgumentOutOfRangeException(nameof(value), "ItemHeight must not be less than 0 or more than 256."); + + _customHeight = value; + + OnCustomHeightChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the description of the menu item. + /// + [Category(Categories.Data)] + [Description("The description of the menu item.")] + [DefaultValue("")] + [Localizable(false)] + public virtual string Description + { + get => _description; + set + { + if (Description != value) + { + _description = value; + + OnDescriptionChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value indicating whether or not the menu item is enabled. + /// + [Category(Categories.Behavior)] + [Description("Value indicating whether or not the menu item is enabled.")] + [DefaultValue(true)] + [Localizable(false)] + public virtual bool Enabled + { + get => _enabled; + set + { + if (Enabled != value) + { + _enabled = value; + + OnEnabledChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value indicating whether or not this menu item should be highlighted when opened. + /// + [Category(Categories.Behavior)] + [Description("Value indicating whether or not this menu item should be highlighted when opened.")] + [DefaultValue(false)] + [Localizable(false)] + public virtual bool Highlight + { + get => _highlight; + set + { + if (Highlight != value) + { + _highlight = value; + + OnHighlightChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the image to be shown in the menu item. + /// + [Category(Categories.Appearance)] + [Description("The image to be shown in the menu item.")] + [DefaultValue(null)] + [Localizable(false)] + public virtual Image Image + { + get => _image; + set + { + if (Image != value) + { + _image = value; + + OnImageChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value from indicating the alignment of an image when specified. + /// + [Category(Categories.Appearance)] + [Description("Value indicating the alignment of an image when specified.")] + [DefaultValue(typeof(BetterMenuButtonImageAlign), "MiddleRight")] + [Localizable(false)] + public virtual BetterMenuButtonImageAlign ImageAlign + { + get => _imageAlign; set + { + if (ImageAlign != value) + { + _imageAlign = value; + + OnImageAlignChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the index of the image from the menu image list to be shown in the menu item. + /// + [Category(Categories.Appearance)] + [Description("The index of the image from the menu image list to be shown in the menu item.")] + [DefaultValue(-1)] + [Localizable(false)] + [Editor("ImageIndexEditor", typeof(UITypeEditor))] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [TypeConverter(typeof(NoneExcludedImageIndexConverter))] + [RelatedImageList("OwnerToolbar.ImageList")] + public virtual int ImageIndex + { + get => ImageIndexer.Index; + set + { + if (ImageIndex != value) + { + ImageIndexer.Index = value; + + OnImageIndexChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets the image indexer used to provide a mechanism for a toolbar to support using both image indexes and image keys. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + private protected BetterMenuButtonImageIndexer ImageIndexer => _imageIndexer; + + /// + /// Gets or sets the key of the image from the menu image list to be shown in the menu item. + /// + [Category(Categories.Appearance)] + [Description("The key of the image from the menu image list to be shown in the menu item.")] + [DefaultValue("")] + [Localizable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [TypeConverter(typeof(ImageKeyConverter))] + [RelatedImageList("OwnerToolbar.ImageList")] + public virtual string ImageKey + { + get => ImageIndexer.Key; + set + { + if (ImageKey != value) + { + ImageIndexer.Key = value; + + OnImageKeyChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets the mnemonic of the menu item text, if any. + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public char Mnemonic => StringUtilities.GetMnemonic(Text, true); + + /// + /// Gets or sets a value indicating whether or not owner draw is enabled for the menu item. + /// + [Category(Categories.Behavior)] + [Description("Value indicating whether or not owner draw is enabled for the menu item.")] + [DefaultValue(false)] + [Localizable(false)] + public bool OwnerDraw + { + get => _ownerDraw; + set + { + if (OwnerDraw != value) + { + _ownerDraw = value; + + OnOwnerDrawChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the shortcut key combination associated with the menu item. + /// + [Category(Categories.Behavior)] + [Description("The shortcut key combination associated with the menu item.")] + [DefaultValue(typeof(Shortcut), "None")] + [Localizable(false)] + public virtual Shortcut Shortcut + { + get => _shortcut; + set + { + if (Shortcut != value) + { + _shortcut = value; + + OnShortcutChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value indicating whether or not the shortcut is shown in the menu item. + /// + [Category(Categories.Behavior)] + [Description("Value indicating whether or not the shortcut is shown in the menu item.")] + [DefaultValue(true)] + [Localizable(false)] + public virtual bool ShowShortcut + { + get => _showShortcut; + set + { + if (ShowShortcut != value) + { + _showShortcut = value; + + OnShowShortcutChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets the text of the menu item. + /// + [Category(Categories.Appearance)] + [Description("The text of the menu item.")] + [DefaultValue("")] + [Localizable(false)] + public virtual string Text + { + get => _text; + set + { + if (Text != value) + { + _text = value; + + OnTextChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets or sets a value indicating whether or not the menu item should use the system shield image. This will take precedence over any value given in , or . + /// + [Category(Categories.Appearance)] + [Description("Value indicating whether or not the menu item should use the system shield image.")] + [DefaultValue(false)] + [Localizable(false)] + public virtual bool UseShieldImage + { + get => _useShieldImage; + set + { + if (UseShieldImage != value) + { + _useShieldImage = value; + + OnUseShieldImageChanged(EventArgs.Empty); + } + } + } + + /// + /// Simulates clicking the menu item. + /// + public void PerformClick() + { + if (Enabled) + { + MenuRoot.DescendantClicked(this); + OnClick(EventArgs.Empty); + } + } + + /// + /// This method is raised when this item is focused. + /// + internal void ItemFocused() + { + MenuRoot.DescendantFocused(this); + OnFocused(new EventArgs()); + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuItem.NativeStructures.cs b/src/BetterControls/BetterMenu/Items/BetterMenuItem.NativeStructures.cs new file mode 100644 index 0000000..cf5ffc3 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuItem.NativeStructures.cs @@ -0,0 +1,73 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.ComponentModel; + +namespace BetterControls +{ + /// + /// Extend this class to create a menu item. + /// + partial class BetterMenuItem + { + /// + /// Computes the native structure as an instance of . + /// + /// An instance of . + [EditorBrowsable(EditorBrowsableState.Never)] + internal virtual NativeMethods.MENUITEMINFO_T ComputeMenuItemInfoT() + { + NativeMethods.MENUITEMINFO_T structure = new NativeMethods.MENUITEMINFO_T(); + + structure.fMask = NativeMethods.MIIM_ID | NativeMethods.MIIM_STATE | NativeMethods.MIIM_SUBMENU | NativeMethods.MIIM_DATA | NativeMethods.MIIM_FTYPE; + structure.wID = UniqueIdentifier; + + if (Items.Count != 0) + { + bool visible = true; + + foreach (BetterMenu menu in Items) + { + if (menu is BetterMenuButton menuButton && !menuButton.Visible) + { + visible = false; + + break; + } + } + + if(visible) + structure.hSubMenu = Handle; + } + + if (!Visible) + structure.fState |= NativeMethods.MFS_HIDDEN; + + return structure; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuItem.cs b/src/BetterControls/BetterMenu/Items/BetterMenuItem.cs new file mode 100644 index 0000000..2da1fe3 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuItem.cs @@ -0,0 +1,92 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.ComponentModel; +using System.ComponentModel; + +namespace BetterControls +{ + /// + /// Extend this class to create a menu item. + /// + public abstract partial class BetterMenuItem : BetterMenu + { + /// + /// Initialize a new instance of . + /// + private protected BetterMenuItem() { } + + /// + /// Initialize a new instance of . + /// + /// The owner menu as an instance of . + private protected BetterMenuItem(BetterMenu ownerMenu) + : base(ownerMenu) + { } + + private bool _visible = true; + + /// + /// Gets the displayed index of this item in the menu. + /// + [Browsable(false)] + public virtual int DisplayedItemIndex => ItemIndex; + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override BetterMenuItemCollection Items => base.Items; + + /// + /// Gets the collection of menu items for this menu. + /// + [Category(Categories.Behavior)] + [Description("The collection of menu items for this menu.")] + [DefaultValue(null)] + [Localizable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public BetterMenuItemCollection SubItems => Items; + + /// + /// Gets or sets a value indicating whether or not the menu item is visible. + /// + [Category(Categories.Behavior)] + [Localizable(true)] + [DefaultValue(true)] + [Description("Value indicating whether or not the menu item is visible.")] + public bool Visible + { + get => _visible; + set + { + if (Visible != value) + { + _visible = value; + } + } + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuPushButton.cs b/src/BetterControls/BetterMenu/Items/BetterMenuPushButton.cs new file mode 100644 index 0000000..ef40226 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuPushButton.cs @@ -0,0 +1,166 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System.Drawing; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Represents a menu push button. + /// + public class BetterMenuPushButton : BetterMenuButton + { + /// + /// Initialize a new instance of . + /// + public BetterMenuPushButton() { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + public BetterMenuPushButton(string text) + : base(text) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + public BetterMenuPushButton(string text, string description) + : base(text, description) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + public BetterMenuPushButton(string text, int imageIndex) + : base(text, imageIndex) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + public BetterMenuPushButton(string text, string description, int imageIndex) + : base(text, description, imageIndex) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The image to be shown in the menu item. + public BetterMenuPushButton(string text, Image image) + : base(text, image) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The image to be shown in the menu item. + public BetterMenuPushButton(string text, string description, Image image) + : base(text, description, image) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuPushButton(string text, Shortcut shortcut) + : base(text, shortcut) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuPushButton(string text, string description, Shortcut shortcut) + : base(text, description, shortcut) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuPushButton(string text, int imageIndex, Shortcut shortcut) + : base(text, imageIndex, shortcut) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The index of the image from the parent most menu image list to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuPushButton(string text, string description, int imageIndex, Shortcut shortcut) + : base(text, description, imageIndex, shortcut) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The image to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuPushButton(string text, Image image, Shortcut shortcut) + : base(text, image, shortcut) + { } + + /// + /// Initialize a new instance of . + /// + /// The text of the menu item. + /// The description of the menu item. + /// The image to be shown in the menu item. + /// The shortcut key combination associated with the menu item. + public BetterMenuPushButton(string text, string description, Image image, Shortcut shortcut) + : base(text, description, image, shortcut) + { } + + /// + /// Initialize a new instance of . + /// + /// The owner menu as an instance of . + private protected BetterMenuPushButton(BetterMenu ownerMenu) + : base(ownerMenu) + { } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuSeparator.NativeStructures.cs b/src/BetterControls/BetterMenu/Items/BetterMenuSeparator.NativeStructures.cs new file mode 100644 index 0000000..6c2d611 --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuSeparator.NativeStructures.cs @@ -0,0 +1,47 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +namespace BetterControls +{ + /// + /// Represents a menu separator. + /// + partial class BetterMenuSeparator + { + /// + /// + /// + /// + internal override NativeMethods.MENUITEMINFO_T ComputeMenuItemInfoT() + { + NativeMethods.MENUITEMINFO_T menuItem = base.ComputeMenuItemInfoT(); + + menuItem.fType |= NativeMethods.MFT_SEPARATOR; + + return menuItem; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterMenu/Items/BetterMenuSeparator.cs b/src/BetterControls/BetterMenu/Items/BetterMenuSeparator.cs new file mode 100644 index 0000000..693824e --- /dev/null +++ b/src/BetterControls/BetterMenu/Items/BetterMenuSeparator.cs @@ -0,0 +1,53 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System.ComponentModel; + +namespace BetterControls +{ + /// + /// Represents a menu separator. + /// + public partial class BetterMenuSeparator : BetterMenuItem + { + /// + /// Initialize a new instance of . + /// + public BetterMenuSeparator() { } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override BetterMenuItemCollection Items => base.Items; + + /// + /// Initialize a new instance of . + /// + /// The owner menu as an instance of . + private protected BetterMenuSeparator(BetterMenu ownerMenu) + : base(ownerMenu) + { } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbar.Collection.cs b/src/BetterControls/BetterToolbar/BetterToolbar.Collection.cs index 7312abb..ba7745a 100644 --- a/src/BetterControls/BetterToolbar/BetterToolbar.Collection.cs +++ b/src/BetterControls/BetterToolbar/BetterToolbar.Collection.cs @@ -77,9 +77,9 @@ private protected void Insert(BetterToolbarItem item) if (IsHandleCreated) { NativeMethods.TBBUTTON structure = item.ComputeTbButton(); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_INSERTBUTTON, item.UniqueIdentifier, ref structure); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_INSERTBUTTON, item.UniqueIdentifier, ref structure); - SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_AUTOSIZE, 0, 0); UpdateItemDimensions(); } } @@ -114,12 +114,12 @@ internal void PerformItemsChanged(CollectionElementItemChangedFlags flags, param throw new ArgumentNullException(nameof(items)); } - SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_AUTOSIZE, 0, 0); if (!AutoSizeItems) { - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONSIZE, 0, NativeMethods.Util.MAKELPARAM(0, ItemHeight)); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETBUTTONSIZE, 0, NativeMethods.Util.MAKELPARAM(0, ItemHeight)); } - SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_AUTOSIZE, 0, 0); if ((flags & CollectionElementItemChangedFlags.RecreateHandle) != 0) RecreateHandle(); diff --git a/src/BetterControls/BetterToolbar/BetterToolbar.Events.cs b/src/BetterControls/BetterToolbar/BetterToolbar.Events.cs new file mode 100644 index 0000000..ae2d901 --- /dev/null +++ b/src/BetterControls/BetterToolbar/BetterToolbar.Events.cs @@ -0,0 +1,270 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.ComponentModel; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Toolbar classes. + /// + partial class BetterToolbar + { + /// + /// This method is raised before a drop-down button has been clicked with the intention of showing the drop-down menu. + /// + /// The event arguments as an instance of + protected virtual void OnBeforeMenuDroppedDown(BetterToolbarMenuDroppedDownEventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + BeforeMenuDroppedDown?.Invoke(this, e); + } + + /// + /// This method is raised after a drop-down button has been clicked and has shown the drop-down menu. + /// + /// The event arguments as an instance of + protected virtual void OnAfterMenuDroppedDown(BetterToolbarMenuDroppedDownEventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + AfterMenuDroppedDown?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnAppearanceChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + AppearanceChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnBorderStyleChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + BorderStyleChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnDividerChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + DividerChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnDropDownArrowsChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + DropDownArrowsChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnImageListChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ImageListChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnImageSizeChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ImageSizeChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnShowToolTipsChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + ShowToolTipsChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnTextAlignChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + TextAlignChanged?.Invoke(this, e); + } + + /// + /// This method is raised when has been changed. + /// + /// The event arguments as an instance of . + protected virtual void OnWrappableChanged(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + WrappableChanged?.Invoke(this, e); + } + + /// + /// This event is raised before a drop-down button has been clicked with the intention of showing the drop-down menu. + /// + public event EventHandler BeforeMenuDroppedDown; + + /// + /// This event is raised after a drop-down button has been clicked and has shown the drop-down menu. + /// + public event EventHandler AfterMenuDroppedDown; + + /// + /// This event is raised has been changed. + /// + public event EventHandler AppearanceChanged; + + /// + /// This event is raised has been changed. + /// + public event EventHandler BorderStyleChanged; + + /// + /// This event is raised has been changed. + /// + public event EventHandler DividerChanged; + + /// + /// This event is raised has been changed. + /// + public event EventHandler DropDownArrowsChanged; + + /// + /// This event is raised after has been changed. + /// + public event EventHandler ImageListChanged; + + /// + /// This event is raised after has been changed. + /// + public event EventHandler ImageSizeChanged; + + /// + /// This event is raised after has been changed. + /// + public event EventHandler ShowToolTipsChanged; + + /// + /// This event is raised after has been changed. + /// + public event EventHandler TextAlignChanged; + + /// + /// This event is raised after has been changed. + /// + public event EventHandler WrappableChanged; + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged + { + add => base.ImeModeChanged += value; + remove => base.ImeModeChanged -= value; + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ForeColorChanged + { + add => base.ForeColorChanged += value; + remove => base.ForeColorChanged -= value; + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler RightToLeftChanged + { + add => base.RightToLeftChanged += value; + remove => base.RightToLeftChanged -= value; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbar.MessageLoop.cs b/src/BetterControls/BetterToolbar/BetterToolbar.MessageLoop.cs new file mode 100644 index 0000000..d400cc6 --- /dev/null +++ b/src/BetterControls/BetterToolbar/BetterToolbar.MessageLoop.cs @@ -0,0 +1,263 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Toolbar classes. + /// + partial class BetterToolbar + { + /// + /// + /// + /// + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_COMMAND + NativeMethods.WM_REFLECT: + if (WmReflectCommand(ref m)) + return; + + break; + case NativeMethods.WM_NOTIFY: + if (WmNotify(ref m)) + return; + + break; + case NativeMethods.WM_NOTIFY + NativeMethods.WM_REFLECT: + if (WmReflectNotify(ref m)) + return; + + break; + } + + base.WndProc(ref m); + } + + /// + /// Handles the WM_COMMAND + WM_REFLECT message. + /// + private protected virtual bool WmReflectCommand(ref Message m) + { + int index = NativeMethods.Util.LOWORD(m.WParam); + + if (Items[index] is BetterToolbarClickableButton button) + button.PerformClick(); + + ResetMouseEventArgs(); + + return false; + } + + ///// + ///// Handles the WM_MENUSELECT message. + ///// + //private protected virtual bool WmMenuSelect(ref Message m) + //{ + // // If the menu item is a popup item (i.e. it has sub-items) then the data sent + // // with WM_MENUSELECT to identify which menu was selected differs than any other + // // menu item. An index to the popup item is sent instead of command identifier. + // int identifier = NativeMethods.Util.LOWORD(m.WParam); + // int flags = NativeMethods.Util.HIWORD(m.WParam); + + // if ((flags & NativeMethods.MF_POPUP) != 0) + // { + // // The item that was selected has sub-items; therefore, we have to identify + // // it recursively using the internal menu tree. + // BetterMenuItem item = GetMenuItemByIndex(m.LParam, identifier); + + // if (item is BetterMenuButton button) + // button.ItemFocused(); + // } + // else + // { + // // The item that was selected does not have any sub-items; therefore, we can + // // identify it using its command identifier. + // GlobalCommand globalCommand = GlobalCommand.GetCommandExecutor(identifier); + + // if (globalCommand != null && globalCommand.CommandExecutor is BetterMenuReference menuReference && menuReference.Menu is BetterMenuButton button) + // button.ItemFocused(); + // } + + // return false; + //} + + /// + /// Handles the WM_NOTIFY message. + /// + private protected virtual bool WmNotify(ref Message m) => WmReflectNotify(ref m); + + /// + /// Handles the WM_NOTIFY + WM_REFLECT message. + /// + private protected virtual unsafe bool WmReflectNotify(ref Message m) + { + NativeMethods.NMHDR* note = (NativeMethods.NMHDR*)m.LParam; + + switch (note->code) + { + case NativeMethods.TBN_QUERYINSERT: + m.Result = (IntPtr)1; + break; + + case NativeMethods.TBN_DROPDOWN: + TbnDropDown(ref m); + break; + case NativeMethods.TTN_NEEDTEXTA: + TtnNeedTextA(ref m); + m.Result = (IntPtr)1; + break; + + case NativeMethods.TTN_NEEDTEXTW: + if (Marshal.SystemDefaultCharSize == 2) + { + TtnNeedTextW(ref m); + m.Result = (IntPtr)1; + } + break; + case NativeMethods.TBN_HOTITEMCHANGE: + TbnHotItemChange(ref m); + break; + case NativeMethods.NM_CUSTOMDRAW: + if (!AutoSizeItems) + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONSIZE, 0, NativeMethods.Util.MAKELPARAM(0, ItemHeight)); + + return true; + } + + return false; + } + + /// + /// Handles the TBN_DROPDOWN message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. + /// + private protected virtual bool TbnDropDown(ref Message m) + { + NativeMethods.NMTOOLBAR note = (NativeMethods.NMTOOLBAR)m.GetLParam(typeof(NativeMethods.NMTOOLBAR)); + + BetterToolbarItem item = Items.GetItemByUniqueIdentifier(note.iItem); + + if (item != null && item is BetterToolbarDropDownButton button) + PerformDropDown(button, false); + + return false; + } + + /// + /// Handles the TTN_NEEDTEXTA message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. + /// + private protected virtual bool TtnNeedTextA(ref Message m) => TtnNeedTextW(ref m); + + /// + /// Handles the TTN_NEEDTEXTW message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. + /// + private protected virtual bool TtnNeedTextW(ref Message m) + { + NativeMethods.TOOLTIPTEXT toolTipText = (NativeMethods.TOOLTIPTEXT)m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); + + BetterToolbarItem item = Items.GetItemByUniqueIdentifier((int)toolTipText.hdr.idFrom); + + // Only items that are buttons (i.e. not a separator) are capable + // of showing tool tips. + if (item != null && item is BetterToolbarButton button) + { + toolTipText.hinst = IntPtr.Zero; + + if (button.ToolTipText != null) + toolTipText.lpszText = GetToolTipText(button); + + if (string.IsNullOrEmpty(toolTipText.lpszText)) + toolTipText.lpszText = null; + + if (RightToLeft == RightToLeft.Yes) + toolTipText.uFlags |= NativeMethods.TTF_RTLREADING; + + Marshal.StructureToPtr(toolTipText, m.LParam, false); + } + + return false; + } + + /// + /// Handles the TBN_HOTITEMCHANGE message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. + /// + private protected virtual bool TbnHotItemChange(ref Message m) + { + // Should we set the hot item? + NativeMethods.NMTBHOTITEM nmTbHotItem = (NativeMethods.NMTBHOTITEM)m.GetLParam(typeof(NativeMethods.NMTBHOTITEM)); + + BetterToolbarItem item = Items.GetItemByUniqueIdentifier(nmTbHotItem.idNew); + + if (item != null && item is BetterToolbarButton button) + { + if (NativeMethods.HICF_ENTERING == (nmTbHotItem.dwFlags & NativeMethods.HICF_ENTERING)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_LEAVING == (nmTbHotItem.dwFlags & NativeMethods.HICF_LEAVING)) + { + _hotButton = null; + } + else if (NativeMethods.HICF_MOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_MOUSE)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_ARROWKEYS == (nmTbHotItem.dwFlags & NativeMethods.HICF_ARROWKEYS)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_ACCELERATOR == (nmTbHotItem.dwFlags & NativeMethods.HICF_ACCELERATOR)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_DUPACCEL == (nmTbHotItem.dwFlags & NativeMethods.HICF_DUPACCEL)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_RESELECT == (nmTbHotItem.dwFlags & NativeMethods.HICF_RESELECT)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_LMOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_LMOUSE)) + { + _hotButton = button; + } + else if (NativeMethods.HICF_TOGGLEDROPDOWN == (nmTbHotItem.dwFlags & NativeMethods.HICF_TOGGLEDROPDOWN)) + { + _hotButton = button; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbar.cs b/src/BetterControls/BetterToolbar/BetterToolbar.cs index 7aa0592..fcf4a05 100644 --- a/src/BetterControls/BetterToolbar/BetterToolbar.cs +++ b/src/BetterControls/BetterToolbar/BetterToolbar.cs @@ -26,7 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using BetterControls.ComponentModel; using System; -using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; @@ -49,7 +48,6 @@ public partial class BetterToolbar : BetterToolbarBase, ISupportInitialize /// public BetterToolbar() { - SetStyle(ControlStyles.UserPaint, false); SetStyle(ControlStyles.FixedHeight, AutoSize); SetStyle(ControlStyles.FixedWidth, false); @@ -463,7 +461,7 @@ public virtual ImageList MenuImageList /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public virtual int Rows => unchecked((int)(long)SendMessage(NativeMethods.TB_GETROWS, 0, 0)); + public virtual int Rows => unchecked((int)(long)UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_GETROWS, 0, 0)); /// /// Gets or sets a value indicating whether or not tool tips should be shown. @@ -539,6 +537,13 @@ public virtual bool Wrappable } } + + + + + + + /// /// /// @@ -546,46 +551,59 @@ protected override CreateParams CreateParams { get { - CreateParams cp = base.CreateParams; + CreateParams parameters = base.CreateParams; - cp.ClassName = NativeMethods.WC_TOOLBAR; - cp.Style |= NativeMethods.CCS_NOPARENTALIGN | NativeMethods.CCS_NORESIZE; + parameters.ClassName = NativeMethods.WC_TOOLBAR; + parameters.Style |= NativeMethods.CCS_NOPARENTALIGN | NativeMethods.CCS_NORESIZE; if (!Divider) - cp.Style |= NativeMethods.CCS_NODIVIDER; + parameters.Style |= NativeMethods.CCS_NODIVIDER; if (Wrappable) - cp.Style |= NativeMethods.TBSTYLE_WRAPPABLE; + parameters.Style |= NativeMethods.TBSTYLE_WRAPPABLE; if (ShowToolTips && !DesignMode) - cp.Style |= NativeMethods.TBSTYLE_TOOLTIPS; + parameters.Style |= NativeMethods.TBSTYLE_TOOLTIPS; - cp.ExStyle &= ~NativeMethods.WS_EX_CLIENTEDGE; - cp.Style &= ~NativeMethods.WS_BORDER; + parameters.ExStyle &= ~NativeMethods.WS_EX_CLIENTEDGE; + parameters.Style &= ~NativeMethods.WS_BORDER; if (BorderStyle == BorderStyle.Fixed3D) - cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; + parameters.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; else if (BorderStyle == BorderStyle.FixedSingle) - cp.Style |= NativeMethods.WS_BORDER; + parameters.Style |= NativeMethods.WS_BORDER; if (Appearance == BetterToolbarAppearance.Flat) - cp.Style |= NativeMethods.TBSTYLE_FLAT; + parameters.Style |= NativeMethods.TBSTYLE_FLAT; if (TextAlign == BetterToolbarTextAlign.Right) - cp.Style |= NativeMethods.TBSTYLE_LIST; + parameters.Style |= NativeMethods.TBSTYLE_LIST; - return cp; + return parameters; } } /// /// /// - protected override void CreateHandle() + private protected override void InitializeCommonControls() { - InitializeCommonControls(); + if (RecreatingHandle) + return; + + IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); + + try + { + NativeMethods.INITCOMMONCONTROLSEX initializeCommonControls = new NativeMethods.INITCOMMONCONTROLSEX(); + initializeCommonControls.dwICC = NativeMethods.ICC_BAR_CLASSES; - base.CreateHandle(); + SafeNativeMethods.InitCommonControlsEx(initializeCommonControls); + } + finally + { + UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); + } } /// @@ -598,20 +616,20 @@ protected override void OnHandleCreated(EventArgs e) // Setup the toolbar based on the state of properties. These properties may have been set before // a handle was created, so setting them would have had no effect on the toolbar. - SendMessage(NativeMethods.TB_BUTTONSTRUCTSIZE, Marshal.SizeOf(typeof(NativeMethods.TBBUTTON)), 0); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_BUTTONSTRUCTSIZE, Marshal.SizeOf(typeof(NativeMethods.TBBUTTON)), 0); // The default is no drop-down arrows, so this is only necessary if drop-down arrows are enabled. if (DropDownArrows) - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETEXTENDEDSTYLE, 0, NativeMethods.TBSTYLE_EX_DRAWDDARROWS); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETEXTENDEDSTYLE, 0, NativeMethods.TBSTYLE_EX_DRAWDDARROWS); if (ImageList != null) { - SendMessage(NativeMethods.TB_SETIMAGELIST, 0, ImageList.Handle); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETIMAGELIST, 0, ImageList.Handle); } else { - SendMessage(NativeMethods.TB_SETIMAGELIST, 0, IntPtr.Zero); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_USER + 32, 0, NativeMethods.Util.MAKELONG(ImageSize.Width, ImageSize.Height)); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETIMAGELIST, 0, IntPtr.Zero); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.WM_USER + 32, 0, NativeMethods.Util.MAKELONG(ImageSize.Width, ImageSize.Height)); } InitializeItems(); @@ -624,28 +642,6 @@ protected override void OnHandleCreated(EventArgs e) EndUpdate(); } - /// - /// Initializes common controls for the toolbar classes. - /// - private void InitializeCommonControls() - { - if (!RecreatingHandle) - { - IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); - - try - { - NativeMethods.INITCOMMONCONTROLSEX initializeCommonControls = new NativeMethods.INITCOMMONCONTROLSEX(); - initializeCommonControls.dwICC = NativeMethods.ICC_BAR_CLASSES; - - SafeNativeMethods.InitCommonControlsEx(initializeCommonControls); - } - finally - { - UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); - } - } - } /// /// Initializes items on the toolbar. @@ -661,11 +657,11 @@ private void InitializeItems() { NativeMethods.TBBUTTON structure = item.ComputeTbButton(); - Marshal.StructureToPtr(structure, (IntPtr)checked((long)items + (structureSize * item.ItemIndex)), true); + Marshal.StructureToPtr(structure, (IntPtr)checked((long)items + (structureSize * item.DisplayedItemIndex)), true); } - SendMessage(NativeMethods.TB_ADDBUTTONS, Items.Count, items); - SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_ADDBUTTONS, Items.Count, items); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_AUTOSIZE, 0, 0); UpdateItemDimensions(); AdjustSize(Dock); @@ -708,7 +704,7 @@ protected override void Dispose(bool disposing) private protected void PerformCancelMode() { if (IsHandleCreated) - SendMessage(NativeMethods.WM_CANCELMODE, 0, 0); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.WM_CANCELMODE, 0, 0); } /// @@ -723,10 +719,16 @@ private protected void PerformCancelMode() public void FocusButton(BetterToolbarButton button) { if (IsHandleCreated) + { if (button is null) - SendMessage(NativeMethods.TB_SETHOTITEM, -1, 0); + { + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETHOTITEM, -1, 0); + } else - SendMessage(NativeMethods.TB_SETHOTITEM, button.ItemIndex, 0); + { + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETHOTITEM, button.DisplayedItemIndex, 0); + } + } } /// @@ -833,16 +835,16 @@ private BetterMenuItem GetMenuItemByIndex(IntPtr handle, int index) { BetterMenuItem item = GetMenuItemByIndex(childHandle, i++); - if (item != null && item.Parent != null && item.Parent is BetterMenuItem parentItem) + if (item != null && item.OwnerElement != null && item.OwnerElement is BetterMenuItem parentItem) return parentItem; } } else { // This menu item does not have any sub-items. - ICommandExecutor commandExecutor = Command.GetItem(identifier); + GlobalCommand globalCommand = GlobalCommand.GetCommandExecutor(identifier); - if (commandExecutor != null && commandExecutor is BetterMenuReference menuReference && menuReference.Menu is BetterMenuItem item) + if (globalCommand != null && globalCommand.CommandExecutor is BetterMenuReference menuReference && menuReference.Menu is BetterMenuItem item) return item; } @@ -862,7 +864,7 @@ protected internal IntPtr AddString(string text) } if (text.Length > 0) - return SendMessage(NativeMethods.TB_ADDSTRING, 0, text + '\0'.ToString()); + return UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_ADDSTRING, 0, text + '\0'.ToString()); return (IntPtr)(-1); } @@ -894,7 +896,7 @@ private protected virtual void PerformDropDownItemSelect(BetterMenuButton item) throw new ArgumentNullException(nameof(item)); } - item.PerformSelect(); + item.ItemFocused(); } /// @@ -972,6 +974,7 @@ protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { _currentScaleDX = factor.Width; _currentScaleDY = factor.Height; + base.ScaleControl(factor, specified); } @@ -1022,15 +1025,6 @@ protected override void SetBoundsCore(int x, int y, int width, int height, Bound base.SetBoundsCore(x, y, width, height, specified); } - /// - /// - /// - /// - protected override void OnSizeChanged(EventArgs e) - { - base.OnSizeChanged(e); - } - /// /// /// @@ -1039,10 +1033,7 @@ protected override void OnSizeChanged(EventArgs e) /// /// /// - void ISupportInitialize.EndInit() - { - EndUpdate(); - } + void ISupportInitialize.EndInit() => EndUpdate(); /// /// Ensures that the dimensions for each item in the toolbar are correct. @@ -1081,247 +1072,8 @@ public void UpdateItemDimensions() AdjustSize(Dock); } - /// - /// This method is raised before a drop-down button has been clicked with the intention of showing the drop-down menu. - /// - /// The event arguments as an instance of - protected virtual void OnBeforeMenuDroppedDown(BetterToolbarMenuDroppedDownEventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - BeforeMenuDroppedDown?.Invoke(this, e); - } - - /// - /// This method is raised after a drop-down button has been clicked and has shown the drop-down menu. - /// - /// The event arguments as an instance of - protected virtual void OnAfterMenuDroppedDown(BetterToolbarMenuDroppedDownEventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - AfterMenuDroppedDown?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnAppearanceChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - AppearanceChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnBorderStyleChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - BorderStyleChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnDividerChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - DividerChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnDropDownArrowsChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - DropDownArrowsChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnImageListChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - ImageListChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnImageSizeChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - ImageSizeChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnShowToolTipsChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - ShowToolTipsChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnTextAlignChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - TextAlignChanged?.Invoke(this, e); - } - - /// - /// This method is raised when has been changed. - /// - /// The event arguments as an instance of . - protected virtual void OnWrappableChanged(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - WrappableChanged?.Invoke(this, e); - } - - /// - /// This event is raised before a drop-down button has been clicked with the intention of showing the drop-down menu. - /// - public event EventHandler BeforeMenuDroppedDown; - - /// - /// This event is raised after a drop-down button has been clicked and has shown the drop-down menu. - /// - public event EventHandler AfterMenuDroppedDown; - - /// - /// This event is raised has been changed. - /// - public event EventHandler AppearanceChanged; - - /// - /// This event is raised has been changed. - /// - public event EventHandler BorderStyleChanged; - - /// - /// This event is raised has been changed. - /// - public event EventHandler DividerChanged; + - /// - /// This event is raised has been changed. - /// - public event EventHandler DropDownArrowsChanged; - - /// - /// This event is raised after has been changed. - /// - public event EventHandler ImageListChanged; - - /// - /// This event is raised after has been changed. - /// - public event EventHandler ImageSizeChanged; - - /// - /// This event is raised after has been changed. - /// - public event EventHandler ShowToolTipsChanged; - - /// - /// This event is raised after has been changed. - /// - public event EventHandler TextAlignChanged; - - /// - /// This event is raised after has been changed. - /// - public event EventHandler WrappableChanged; - - /// - /// Occurs when the property has changed. - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler ImeModeChanged - { - add => base.ImeModeChanged += value; - remove => base.ImeModeChanged -= value; - } - - /// - /// Occurs when the property has changed. - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler ForeColorChanged - { - add => base.ForeColorChanged += value; - remove => base.ForeColorChanged -= value; - } - - /// - /// Occurs when the property value changes. - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler RightToLeftChanged - { - add => base.RightToLeftChanged += value; - remove => base.RightToLeftChanged -= value; - } /// /// This event handler is raised when the handle associated with image list associated with this toolbar is recreated. @@ -1359,237 +1111,5 @@ private void DetachImageListHandle(object sender, EventArgs e) ImageList = null; } - - private HashSet constants = new HashSet(); - - /// - /// - /// - /// - protected override void WndProc(ref Message m) - { - switch (m.Msg) - { - case NativeMethods.WM_COMMAND + NativeMethods.WM_REFLECT: - if (WmReflectCommand(ref m)) - return; - - break; - case NativeMethods.WM_MENUSELECT: - if (WmMenuSelect(ref m)) - return; - - break; - case NativeMethods.WM_NOTIFY: - if (WmNotify(ref m)) - return; - - break; - case NativeMethods.WM_NOTIFY + NativeMethods.WM_REFLECT: - if (WmReflectNotify(ref m)) - return; - - break; - } - - base.WndProc(ref m); - } - - /// - /// Handles the WM_COMMAND + WM_REFLECT message. - /// - private protected virtual bool WmReflectCommand(ref Message m) - { - int index = NativeMethods.Util.LOWORD(m.WParam); - - if (Items[index] is BetterToolbarClickableButton button) - button.PerformClick(); - - ResetMouseEventArgs(); - - return false; - } - - /// - /// Handles the WM_MENUSELECT message. - /// - private protected virtual bool WmMenuSelect(ref Message m) - { - // If the menu item is a popup item (i.e. it has sub-items) then the data sent - // with WM_MENUSELECT to identify which menu was selected differs than any other - // menu item. An index to the popup item is sent instead of command identifier. - int identifier = NativeMethods.Util.LOWORD(m.WParam); - int flags = NativeMethods.Util.HIWORD(m.WParam); - - if ((flags & NativeMethods.MF_POPUP) != 0) - { - // The item that was selected has sub-items; therefore, we have to identify - // it recursively using the internal menu tree. - BetterMenuItem item = GetMenuItemByIndex(m.LParam, identifier); - - if (item is BetterMenuButton button) - button.PerformSelect(); - } - else - { - // The item that was selected does not have any sub-items; therefore, we can - // identify it using its command identifier. - ICommandExecutor commandExecutor = Command.GetItem(identifier); - - if (commandExecutor != null && commandExecutor is BetterMenuReference menuReference && menuReference.Menu is BetterMenuButton button) - button.PerformSelect(); - } - - return false; - } - - /// - /// Handles the WM_NOTIFY message. - /// - private protected virtual bool WmNotify(ref Message m) => WmReflectNotify(ref m); - - /// - /// Handles the WM_NOTIFY + WM_REFLECT message. - /// - private protected virtual unsafe bool WmReflectNotify(ref Message m) - { - NativeMethods.NMHDR* note = (NativeMethods.NMHDR*)m.LParam; - - switch (note->code) - { - case NativeMethods.TBN_QUERYINSERT: - m.Result = (IntPtr)1; - break; - - case NativeMethods.TBN_DROPDOWN: - TbnDropDown(ref m); - break; - case NativeMethods.TTN_NEEDTEXTA: - TtnNeedTextA(ref m); - m.Result = (IntPtr)1; - break; - - case NativeMethods.TTN_NEEDTEXTW: - if (Marshal.SystemDefaultCharSize == 2) - { - TtnNeedTextW(ref m); - m.Result = (IntPtr)1; - } - break; - case NativeMethods.TBN_HOTITEMCHANGE: - TbnHotItemChange(ref m); - break; - case NativeMethods.NM_CUSTOMDRAW: - if (!AutoSizeItems) - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONSIZE, 0, NativeMethods.Util.MAKELPARAM(0, ItemHeight)); - - return true; - } - - return false; - } - - /// - /// Handles the TBN_DROPDOWN message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. - /// - private protected virtual bool TbnDropDown(ref Message m) - { - NativeMethods.NMTOOLBAR note = (NativeMethods.NMTOOLBAR)m.GetLParam(typeof(NativeMethods.NMTOOLBAR)); - - BetterToolbarItem item = Items.GetItemByUniqueIdentifier(note.iItem); - - if (item != null && item is BetterToolbarDropDownButton button) - PerformDropDown(button, false); - - return false; - } - - /// - /// Handles the TTN_NEEDTEXTA message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. - /// - private protected virtual bool TtnNeedTextA(ref Message m) => TtnNeedTextW(ref m); - - /// - /// Handles the TTN_NEEDTEXTW message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. - /// - private protected virtual bool TtnNeedTextW(ref Message m) - { - NativeMethods.TOOLTIPTEXT toolTipText = (NativeMethods.TOOLTIPTEXT)m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT)); - - BetterToolbarItem item = Items.GetItemByUniqueIdentifier((int)toolTipText.hdr.idFrom); - - // Only items that are buttons (i.e. not a separator) are capable - // of showing tool tips. - if (item != null && item is BetterToolbarButton button) - { - toolTipText.hinst = IntPtr.Zero; - - if (button.ToolTipText != null) - toolTipText.lpszText = GetToolTipText(button); - - if (string.IsNullOrEmpty(toolTipText.lpszText)) - toolTipText.lpszText = null; - - if (RightToLeft == RightToLeft.Yes) - toolTipText.uFlags |= NativeMethods.TTF_RTLREADING; - - Marshal.StructureToPtr(toolTipText, m.LParam, false); - } - - return false; - } - - /// - /// Handles the TBN_HOTITEMCHANGE message within the WM_NOTIFY and WM_NOTIFY + WM_REFLECT messages. - /// - private protected virtual bool TbnHotItemChange(ref Message m) - { - // Should we set the hot item? - NativeMethods.NMTBHOTITEM nmTbHotItem = (NativeMethods.NMTBHOTITEM)m.GetLParam(typeof(NativeMethods.NMTBHOTITEM)); - - BetterToolbarItem item = Items.GetItemByUniqueIdentifier(nmTbHotItem.idNew); - - if (item != null && item is BetterToolbarButton button) - { - if (NativeMethods.HICF_ENTERING == (nmTbHotItem.dwFlags & NativeMethods.HICF_ENTERING)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_LEAVING == (nmTbHotItem.dwFlags & NativeMethods.HICF_LEAVING)) - { - _hotButton = null; - } - else if (NativeMethods.HICF_MOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_MOUSE)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_ARROWKEYS == (nmTbHotItem.dwFlags & NativeMethods.HICF_ARROWKEYS)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_ACCELERATOR == (nmTbHotItem.dwFlags & NativeMethods.HICF_ACCELERATOR)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_DUPACCEL == (nmTbHotItem.dwFlags & NativeMethods.HICF_DUPACCEL)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_RESELECT == (nmTbHotItem.dwFlags & NativeMethods.HICF_RESELECT)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_LMOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_LMOUSE)) - { - _hotButton = button; - } - else if (NativeMethods.HICF_TOGGLEDROPDOWN == (nmTbHotItem.dwFlags & NativeMethods.HICF_TOGGLEDROPDOWN)) - { - _hotButton = button; - } - } - - return false; - } } } \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbarBase.Events.cs b/src/BetterControls/BetterToolbar/BetterToolbarBase.Events.cs new file mode 100644 index 0000000..2adb543 --- /dev/null +++ b/src/BetterControls/BetterToolbar/BetterToolbarBase.Events.cs @@ -0,0 +1,60 @@ +using System; +using System.ComponentModel; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Wrapper of the Windows Toolbar classes. + /// + partial class BetterToolbarBase + { + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event PaintEventHandler Paint + { + add => base.Paint += value; + remove => base.Paint -= value; + } + + [Browsable(true)] + [EditorBrowsable(EditorBrowsableState.Always)] + public new event EventHandler AutoSizeChanged + { + add => base.AutoSizeChanged += value; + remove => base.AutoSizeChanged -= value; + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler BackColorChanged + { + add => base.BackColorChanged += value; + remove => base.BackColorChanged -= value; + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler BackgroundImageChanged + { + add => base.BackgroundImageChanged += value; + remove => base.BackgroundImageChanged -= value; + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler BackgroundImageLayoutChanged + { + add => base.BackgroundImageLayoutChanged += value; + remove => base.BackgroundImageLayoutChanged -= value; + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TextChanged + { + add => base.TextChanged += value; + remove => base.TextChanged -= value; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbarBase.cs b/src/BetterControls/BetterToolbar/BetterToolbarBase.cs index 51059d5..eb9a537 100644 --- a/src/BetterControls/BetterToolbar/BetterToolbarBase.cs +++ b/src/BetterControls/BetterToolbar/BetterToolbarBase.cs @@ -1,23 +1,19 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace BetterControls { - [DesignerCategory("Code")] - public abstract class BetterToolbarBase : BetterControl + /// + /// Wrapper of the Windows Toolbar classes. + /// + public abstract partial class BetterToolbarBase : BetterCommonControl { /// /// Initialize a new instance of . /// protected BetterToolbarBase() { } - - #region Hidden Properties - - /// - /// - /// + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public override RightToLeft RightToLeft @@ -25,10 +21,7 @@ public override RightToLeft RightToLeft get => base.RightToLeft; set => base.RightToLeft = value; } - - /// - /// - /// + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public override Color BackColor @@ -36,10 +29,7 @@ public override Color BackColor get => base.BackColor; set => base.BackColor = value; } - - /// - /// - /// + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage @@ -47,10 +37,7 @@ public override Image BackgroundImage get => base.BackgroundImage; set => base.BackgroundImage = value; } - - /// - /// - /// + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout @@ -58,10 +45,7 @@ public override ImageLayout BackgroundImageLayout get => base.BackgroundImageLayout; set => base.BackgroundImageLayout = value; } - - /// - /// - /// + [Bindable(false)] [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] @@ -71,20 +55,14 @@ public override string Text get => base.Text; set => base.Text = value; } - - /// - /// - /// + [EditorBrowsable(EditorBrowsableState.Never)] protected override bool DoubleBuffered { get => base.DoubleBuffered; set => base.DoubleBuffered = value; } - - /// - /// - /// + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public override Color ForeColor @@ -92,10 +70,7 @@ public override Color ForeColor get => base.ForeColor; set => base.ForeColor = value; } - - /// - /// - /// + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public new ImeMode ImeMode @@ -103,74 +78,5 @@ public override Color ForeColor get => base.ImeMode; set => base.ImeMode = value; } - - #endregion - - #region Hide Events - - /// - /// - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event PaintEventHandler Paint - { - add => base.Paint += value; - remove => base.Paint -= value; - } - - /// - /// - /// - [Browsable(true)] - [EditorBrowsable(EditorBrowsableState.Always)] - public new event EventHandler AutoSizeChanged - { - add => base.AutoSizeChanged += value; - remove => base.AutoSizeChanged -= value; - } - - /// - /// - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler BackColorChanged - { - add => base.BackColorChanged += value; - remove => base.BackColorChanged -= value; - } - - /// - /// - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler BackgroundImageChanged - { - add => base.BackgroundImageChanged += value; - remove => base.BackgroundImageChanged -= value; - } - - /// - /// - /// - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler BackgroundImageLayoutChanged - { - add => base.BackgroundImageLayoutChanged += value; - remove => base.BackgroundImageLayoutChanged -= value; - } - - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - public new event EventHandler TextChanged - { - add => base.TextChanged += value; - remove => base.TextChanged -= value; - } - - #endregion } } \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbarDropDownMenu.cs b/src/BetterControls/BetterToolbar/BetterToolbarDropDownMenu.cs new file mode 100644 index 0000000..7942d90 --- /dev/null +++ b/src/BetterControls/BetterToolbar/BetterToolbarDropDownMenu.cs @@ -0,0 +1,48 @@ +using System.ComponentModel; +using System.Windows.Forms; + +namespace BetterControls +{ + /// + /// Menu specifically designed for use with drop-down toolbar buttons. + /// + [ToolboxItem(true)] + [DesignTimeVisible(true)] + public class BetterToolbarDropDownMenu : BetterMenuRoot + { + /// + /// Initialize a new instance of . + /// + public BetterToolbarDropDownMenu() { } + + /// + /// Initialize a new instance of . + /// + /// The drop-down button that is associated with this drop-down menu. + public BetterToolbarDropDownMenu(BetterToolbarDropDownButton item) + { + ParentControl = item; + } + + public object ParentControl { get; set; } + + /// + /// + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override ImageList ImageList + { + get + { + if (ParentControl is BetterToolbarDropDownButton item && item.OwnerElement is BetterToolbar toolbar) + return toolbar.MenuImageList; + + return null; + } + set { } + } + + public void Recreate() => RecreateHandle(); + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/BetterToolbarExtensions.cs b/src/BetterControls/BetterToolbar/BetterToolbarExtensions.cs index e77e5e1..09902c4 100644 --- a/src/BetterControls/BetterToolbar/BetterToolbarExtensions.cs +++ b/src/BetterControls/BetterToolbar/BetterToolbarExtensions.cs @@ -25,7 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using System; -using System.Windows.Forms; namespace BetterControls { @@ -35,7 +34,7 @@ namespace BetterControls public static class BetterToolbarExtensions { /// - /// Adds a to a collection of toolbar items. + /// Adds a to the collection of toolbar items. /// /// The collection of toolbar items to add to. public static void AddSeparator(this BetterToolbarItemCollection items) diff --git a/src/BetterControls/BetterToolbar/BetterToolbarItemCollection.cs b/src/BetterControls/BetterToolbar/BetterToolbarItemCollection.cs index bbe7e47..d237d9c 100644 --- a/src/BetterControls/BetterToolbar/BetterToolbarItemCollection.cs +++ b/src/BetterControls/BetterToolbar/BetterToolbarItemCollection.cs @@ -42,13 +42,15 @@ public class BetterToolbarItemCollection : ElementCollection, /// /// Initialize a new instance of . /// - /// The parent toolbar as an instance of . + /// The owner control as an instance of . internal BetterToolbarItemCollection(BetterToolbar ownerToolbar) : base(ownerToolbar) { + _uniqueItems = new HashSet(); _uniqueIdentifierItems = new Dictionary(); } + private HashSet _uniqueItems; private Dictionary _uniqueIdentifierItems; /// @@ -153,6 +155,22 @@ public virtual void Add(string text, string description, int imageIndex) }); } + /// + /// + /// + /// + /// + public override void InsertRange(int index, params BetterToolbarItem[] items) + { + foreach (BetterToolbarItem item in items) + { + if (_uniqueItems.Contains(item)) + throw new ArgumentException("Cannot add or insert the item in more than one place. You must first remove it from its current location or clone it."); + } + + base.InsertRange(index, items); + } + /// /// Gets a from the collection by its identifier. /// @@ -187,6 +205,7 @@ private protected override void PerformItemsAdded(int startIndex, params BetterT item.SetUniqueIdentifier(item.ItemIndex); + _uniqueItems.Add(item); _uniqueIdentifierItems.Add(item.UniqueIdentifier, item); } } @@ -209,6 +228,7 @@ private protected override void PerformItemsRemoved(int startIndex, params Bette { BetterToolbarItem item = items[i]; + _uniqueItems.Remove(item); _uniqueIdentifierItems.Remove(item.UniqueIdentifier); item.ResetUniqueIdentifier(); diff --git a/src/BetterControls/BetterToolbar/BetterToolbarMenuDroppedDownEventArgs.cs b/src/BetterControls/BetterToolbar/BetterToolbarMenuDroppedDownEventArgs.cs new file mode 100644 index 0000000..3dc37f6 --- /dev/null +++ b/src/BetterControls/BetterToolbar/BetterToolbarMenuDroppedDownEventArgs.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterControls +{ + public class BetterToolbarMenuDroppedDownEventArgs + { + public BetterToolbarMenuDroppedDownEventArgs(BetterToolbarDropDownButton item) + { + Item = item ?? throw new ArgumentNullException(nameof(item)); + } + + public BetterToolbarDropDownButton Item { get; } + } +} diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.NativeStructure.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.NativeStructure.cs index c8626fe..48b25a1 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.NativeStructure.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.NativeStructure.cs @@ -1,4 +1,30 @@ -using System.Runtime.InteropServices; +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using System.Runtime.InteropServices; namespace BetterControls { diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.cs index 912b9c8..a9e4d2d 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarButton.cs @@ -125,8 +125,8 @@ private protected BetterToolbarButton(BetterToolbar ownerToolbar) /// /// Gets or sets the description of the button. /// - [Category(Categories.Appearance)] - [Description("The text of the toolbar item.")] + [Category(Categories.Data)] + [Description("The description of the toolbar item.")] [DefaultValue("")] [Localizable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] @@ -165,10 +165,10 @@ public virtual bool Enabled } /// - /// Gets or sets the index of the image from the menu image list to be shown in the menu item. + /// Gets or sets the index of the image from the toolbar image list to be shown in the toolbar item. /// [Category(Categories.Appearance)] - [Description("The index of the image from the menu image list to be shown in the menu item.")] + [Description("The index of the image from the toolbar image list to be shown in the toolbar item.")] [DefaultValue(-1)] [Localizable(false)] [Editor("ImageIndexEditor", typeof(UITypeEditor))] @@ -435,6 +435,22 @@ public virtual void Focus() OwnerToolbar.FocusButton(this); } + private protected override void ConfigureClone(BetterToolbarItem item) + { + base.ConfigureClone(item); + + BetterToolbarButton button = (BetterToolbarButton)item; + + button.Description = Description; + button.Enabled = Enabled; + button.ImageIndex = ImageIndex; + button.ImageKey = ImageKey; + button.Pressed = Pressed; + button.ShowImage = ShowImage; + button.Text = Text; + button.ToolTipText = ToolTipText; + } + /// /// /// diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.Events.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.Events.cs new file mode 100644 index 0000000..e6ca135 --- /dev/null +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.Events.cs @@ -0,0 +1,29 @@ +using System; + +namespace BetterControls +{ + /// + /// Extend this class to create a toolbar item that is a clickable button. + /// + partial class BetterToolbarClickableButton + { + /// + /// This method is raised when the toolbar button is clicked. + /// + /// The event arguments as an instance of . + protected virtual void OnClick(EventArgs e) + { + if (e is null) + { + throw new ArgumentNullException(nameof(e)); + } + + Click?.Invoke(this, e); + } + + /// + /// This event is raised when the toolbar button is clicked. + /// + public event EventHandler Click; + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.cs index 2666331..026d1a1 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarClickableButton.cs @@ -5,7 +5,7 @@ namespace BetterControls /// /// Extend this class to create a toolbar item that is a clickable button. /// - public abstract class BetterToolbarClickableButton : BetterToolbarButton + public abstract partial class BetterToolbarClickableButton : BetterToolbarButton { /// /// Initialize a new instance of . @@ -75,24 +75,5 @@ public void PerformClick() OnClick(EventArgs.Empty); } } - - /// - /// This method is raised when the toolbar button is clicked. - /// - /// The event arguments as an instance of . - protected virtual void OnClick(EventArgs e) - { - if (e is null) - { - throw new ArgumentNullException(nameof(e)); - } - - Click?.Invoke(this, e); - } - - /// - /// This event is raised when the toolbar button is clicked. - /// - public event EventHandler Click; } } \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarDropDownButton.NativeStructure.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarDropDownButton.NativeStructure.cs new file mode 100644 index 0000000..0d610f7 --- /dev/null +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarDropDownButton.NativeStructure.cs @@ -0,0 +1,60 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +namespace BetterControls +{ + /// + /// Represents a toolbar drop-down button. + /// + partial class BetterToolbarDropDownButton + { + /// + /// + /// + /// + internal override NativeMethods.TBBUTTON ComputeTbButton() + { + NativeMethods.TBBUTTON structure = base.ComputeTbButton(); + + structure.fsStyle = NativeMethods.BTNS_DROPDOWN; + + return structure; + } + + /// + /// + /// + /// + internal override NativeMethods.TBBUTTONINFO ComputeTbButtonInfo() + { + NativeMethods.TBBUTTONINFO button = base.ComputeTbButtonInfo(); + + button.fsStyle |= NativeMethods.BTNS_DROPDOWN; + + return button; + } + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarDropDownButton.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarDropDownButton.cs new file mode 100644 index 0000000..5448428 --- /dev/null +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarDropDownButton.cs @@ -0,0 +1,140 @@ +/* COPYRIGHT NOTICE + +MIT License + +Copyright (c) 2022 SharpVNC Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +using BetterControls.ComponentModel; +using System.ComponentModel; + +namespace BetterControls +{ + /// + /// Represents a toolbar drop-down button. + /// + public partial class BetterToolbarDropDownButton : BetterToolbarButton + { + /// + /// Initialize a new instance of . + /// + public BetterToolbarDropDownButton() + { + _dropDownMenu = new BetterToolbarDropDownMenu(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the button. + public BetterToolbarDropDownButton(string text) + : base(text) + { + _dropDownMenu = new BetterToolbarDropDownMenu(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the button. + /// The description of the button. + public BetterToolbarDropDownButton(string text, string description) + : base(text, description) + { + _dropDownMenu = new BetterToolbarDropDownMenu(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the button. + /// The index of the image from the toolbar image list to be shown in the button. + public BetterToolbarDropDownButton(string text, int imageIndex) + : base(text, imageIndex) + { + _dropDownMenu = new BetterToolbarDropDownMenu(this); + } + + /// + /// Initialize a new instance of . + /// + /// The text of the button. + /// The description of the button. + /// The index of the image from the toolbar image list to be shown in the button. + public BetterToolbarDropDownButton(string text, string description, int imageIndex) + : base(text, description, imageIndex) + { + _dropDownMenu = new BetterToolbarDropDownMenu(this); + } + + /// + /// Initialize a new instance of . + /// + /// The owner toolbar as an instance of . + private protected BetterToolbarDropDownButton(BetterToolbar ownerToolbar) + : base(ownerToolbar) + { + _dropDownMenu = new BetterToolbarDropDownMenu(this); + } + + private BetterToolbarDropDownMenu _dropDownMenu; + + /// + /// Gets or sets the drop-down menu for this toolbar button. + /// + [Category(Categories.Behavior)] + [Description("The drop down menu for this toolbar button.")] + [DefaultValue(null)] + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual BetterToolbarDropDownMenu DropDownMenu => _dropDownMenu; + + /// + /// Gets or sets the collection of sub-items for the drop-down button. + /// + [Category(Categories.Behavior)] + [Description("The collection of sub-items for the drop-down button.")] + [DefaultValue(null)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public virtual BetterMenuItemCollection SubItems => DropDownMenu.Items; + + /// + /// + /// + /// + protected override int ComputeAutoSizeWidth() + { + int width = base.ComputeAutoSizeWidth(); + + width += 12; // Accounts for the drop-down arrow + + return width; + } + + /// + /// + /// + /// + private protected override BetterToolbarItem CreateClone() => new BetterToolbarDropDownButton(); + } +} \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarItem.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarItem.cs index 342cd42..b2a08ae 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarItem.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarItem.cs @@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ using BetterControls.ComponentModel; +using System; using System.ComponentModel; using System.Drawing; @@ -36,7 +37,7 @@ namespace BetterControls [ToolboxItem(false)] [DesignTimeVisible(false)] [Designer("BetterToolbarItemDesigner")] - public abstract partial class BetterToolbarItem : BetterToolbarItemBase + public abstract partial class BetterToolbarItem : BetterToolbarItemBase, ICloneable { /// /// Initialize a new instance of . @@ -46,7 +47,7 @@ private protected BetterToolbarItem() { } /// /// Initialize a new instance of . /// - /// The parent toolbar as an instance of . + /// The owner toolbar as an instance of . private protected BetterToolbarItem(BetterToolbar ownerToolbar) : base(ownerToolbar) { } @@ -78,6 +79,21 @@ public virtual bool AutoSize } } + /// + /// Gets the displayed index of this item in the toolbar. + /// + [Browsable(false)] + public virtual int DisplayedItemIndex + { + get + { + if (IsOwnerHandleCreated) + return ItemIndex; + + return -1; + } + } + /// /// Gets or sets a value indicating whether or not the toolbar item is visible. /// @@ -192,7 +208,7 @@ protected override void PerformItemChanged(CollectionElementItemChangedFlags fla { NativeMethods.TBBUTTONINFO structure = ComputeTbButtonInfo(); - UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETBUTTONINFO, ItemIndex, ref structure); + UnsafeNativeMethods.SendMessage(GetHandleRef(), NativeMethods.TB_SETBUTTONINFO, DisplayedItemIndex, ref structure); // Reflect this change to the parent control. OwnerToolbar.PerformItemsChanged(flags, new BetterToolbarItem[] @@ -202,6 +218,41 @@ protected override void PerformItemChanged(CollectionElementItemChangedFlags fla } } + /// + /// + /// + /// + public object Clone() + { + BetterToolbarItem item = CreateClone(); + + ConfigureClone(item); + + return item; + } + + /// + /// Creates a new concrete type that should be cloned. + /// + /// + private protected abstract BetterToolbarItem CreateClone(); + + /// + /// Configures the created concrete type that should be cloned. + /// + /// The concrete type that should be cloned. + private protected virtual void ConfigureClone(BetterToolbarItem item) + { + if (item is null) + { + throw new ArgumentNullException(nameof(item)); + } + + item.AutoSize = AutoSize; + item.Visible = Visible; + item.Width = Width; + } + /// /// /// diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarItemBase.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarItemBase.cs index 5269c5d..4b58836 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarItemBase.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarItemBase.cs @@ -84,7 +84,7 @@ public bool IsOwnerHandleCreated { /// Gets a for the current component. /// /// An instance of . - protected virtual HandleRef GetHandleRef() + protected internal virtual HandleRef GetHandleRef() { if (!IsOwnerHandleCreated) return default; diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarPushButton.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarPushButton.cs index f4aa61f..bd73c44 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarPushButton.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarPushButton.cs @@ -53,5 +53,11 @@ public BetterToolbarPushButton(string text, string description, int imageIndex) private protected BetterToolbarPushButton(BetterToolbar ownerToolbar) : base(ownerToolbar) { } + + /// + /// + /// + /// + private protected override BetterToolbarItem CreateClone() => new BetterToolbarPushButton(); } } \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarSeparator.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarSeparator.cs index 57c3361..b72ab9e 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarSeparator.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarSeparator.cs @@ -52,5 +52,11 @@ protected override int ComputeAutoSizeWidth() { return 8; } + + /// + /// + /// + /// + private protected override BetterToolbarItem CreateClone() => new BetterToolbarSeparator(); } } \ No newline at end of file diff --git a/src/BetterControls/BetterToolbar/Items/BetterToolbarToggleButton.cs b/src/BetterControls/BetterToolbar/Items/BetterToolbarToggleButton.cs index 4553428..a86ae29 100644 --- a/src/BetterControls/BetterToolbar/Items/BetterToolbarToggleButton.cs +++ b/src/BetterControls/BetterToolbar/Items/BetterToolbarToggleButton.cs @@ -52,6 +52,12 @@ public BetterToolbarToggleButton(string text, string description, int imageIndex /// The owner toolbar as an instance of . private protected BetterToolbarToggleButton(BetterToolbar ownerToolbar) : base(ownerToolbar) - { } + { } + + /// + /// + /// + /// + private protected override BetterToolbarItem CreateClone() => new BetterToolbarToggleButton(); } } \ No newline at end of file