#region Imports using DPumpHydr.WinFrmUI.RLT.Child.Material; using DPumpHydr.WinFrmUI.RLT.Controls; using DPumpHydr.WinFrmUI.RLT.Enum.Material; using DPumpHydr.WinFrmUI.RLT.Manager; using DPumpHydr.WinFrmUI.RLT.Util; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Text; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; using static DPumpHydr.WinFrmUI.RLT.Helper.MaterialDrawHelper; using static DPumpHydr.WinFrmUI.RLT.Util.MaterialAnimations; using ButtonState = DPumpHydr.WinFrmUI.RLT.Enum.Material.ButtonState; #endregion namespace DPumpHydr.WinFrmUI.RLT.Forms { #region MaterialForm public class MaterialForm : Form, MaterialControlI { #region Public Properties [Browsable(false)] public int Depth { get; set; } [Browsable(false)] public MaterialSkinManager SkinManager => MaterialSkinManager.Instance; [Browsable(false)] public MaterialMouseState MouseState { get; set; } [Category("Layout")] public bool Sizable { get; set; } [Category("Material"), Browsable(true), DisplayName("Form Style"), DefaultValue(FormStyles.ActionBar_40)] public FormStyles FormStyle { get => _formStyle; set { if (_formStyle == value) { return; } _formStyle = value; RecalculateFormBoundaries(); } } [Category("Drawer")] public Cursor DrawerHamburgerCursor { get; set; } = Cursors.Hand; [Category("Drawer")] public bool DrawerShowIconsWhenHidden { get => _drawerShowIconsWhenHidden; set { if (_drawerShowIconsWhenHidden == value) { return; } _drawerShowIconsWhenHidden = value; if (drawerControl == null) { return; } drawerControl.ShowIconsWhenHidden = _drawerShowIconsWhenHidden; drawerControl.Refresh(); } } [Category("Drawer")] public int DrawerWidth { get; set; } [Category("Drawer")] public bool DrawerAutoHide { get => _drawerAutoHide; set => drawerControl.AutoHide = _drawerAutoHide = value; } [Category("Drawer")] public bool DrawerAutoShow { get => _drawerAutoShow; set => drawerControl.AutoShow = _drawerAutoShow = value; } [Category("Drawer")] public int DrawerIndicatorWidth { get; set; } [Category("Drawer")] public bool DrawerIsOpen { get => _drawerIsOpen; set { if (_drawerIsOpen == value) { return; } _drawerIsOpen = value; if (value) { drawerControl?.Show(); } else { drawerControl?.Hide(); } } } [Category("Drawer")] public bool DrawerUseColors { get => _drawerUseColors; set { if (_drawerUseColors == value) { return; } _drawerUseColors = value; if (drawerControl == null) { return; } drawerControl.UseColors = value; drawerControl.Refresh(); } } [Category("Drawer")] public bool DrawerHighlightWithAccent { get => _drawerHighlightWithAccent; set { if (_drawerHighlightWithAccent == value) { return; } _drawerHighlightWithAccent = value; if (drawerControl == null) { return; } drawerControl.HighlightWithAccent = value; drawerControl.Refresh(); } } [Category("Drawer")] public bool DrawerBackgroundWithAccent { get => _backgroundWithAccent; set { if (_backgroundWithAccent == value) { return; } _backgroundWithAccent = value; if (drawerControl == null) { return; } drawerControl.BackgroundWithAccent = value; drawerControl.Refresh(); } } [Category("Drawer")] public MaterialTabControl DrawerTabControl { get; set; } private string[] _DrawerHideTabName = new List().ToArray(); [Category("Drawer")] public string[] DrawerHideTabName { get => _DrawerHideTabName; set { _DrawerHideTabName = value; drawerControl.DrawerHideTabName = _DrawerHideTabName; } } private System.Windows.Forms.TabPage[] _DrawerNonClickTabPage = new List().ToArray(); [Category("Drawer")] public System.Windows.Forms.TabPage[] DrawerNonClickTabPage { get => _DrawerNonClickTabPage; set { _DrawerNonClickTabPage = value; drawerControl.DrawerNonClickTabPage = _DrawerNonClickTabPage; } } public override string Text { get => base.Text; set { base.Text = value; Invalidate(); } } public new FormWindowState WindowState { get => base.WindowState; set => base.WindowState = value; } public new FormBorderStyle FormBorderStyle { get => base.FormBorderStyle; set => base.FormBorderStyle = value; } public Rectangle UserArea => new(ClientRectangle.X, ClientRectangle.Y + STATUS_BAR_HEIGHT + ACTION_BAR_HEIGHT, ClientSize.Width, ClientSize.Height - (STATUS_BAR_HEIGHT + ACTION_BAR_HEIGHT)); #endregion #region Enums /// /// Window Messages /// /// private enum WM { /// /// WM_NCCALCSIZE /// NonClientCalcSize = 0x0083, /// /// WM_NCACTIVATE /// NonClientActivate = 0x0086, /// /// WM_NCLBUTTONDOWN /// NonClientLeftButtonDown = 0x00A1, /// /// WM_SYSCOMMAND /// SystemCommand = 0x0112, /// /// WM_MOUSEMOVE /// MouseMove = 0x0200, /// /// WM_LBUTTONDOWN /// LeftButtonDown = 0x0201, /// /// WM_LBUTTONUP /// LeftButtonUp = 0x0202, /// /// WM_LBUTTONDBLCLK /// LeftButtonDoubleClick = 0x0203, /// /// WM_RBUTTONDOWN /// RightButtonDown = 0x0204, /// /// WM_ACTIVATEAPP /// ActivateApp = 0x001C } /// /// Hit Test Results /// /// private enum HT { /// /// HTNOWHERE - Nothing under cursor /// None = 0, /// /// HTCAPTION - Titlebar /// Caption = 2, /// /// HTLEFT - Left border /// Left = 10, /// /// HTRIGHT - Right border /// Right = 11, /// /// HTTOP - Top border /// Top = 12, /// /// HTTOPLEFT - Top left corner /// TopLeft = 13, /// /// HTTOPRIGHT - Top right corner /// TopRight = 14, /// /// HTBOTTOM - Bottom border /// Bottom = 15, /// /// HTBOTTOMLEFT - Bottom left corner /// BottomLeft = 16, /// /// HTBOTTOMRIGHT - Bottom right corner /// BottomRight = 17, } /// /// Window Styles /// /// private enum WS { /// /// WS_MINIMIZEBOX - Allow minimizing from taskbar /// MinimizeBox = 0x20000, /// /// WS_SIZEFRAME - Required for Aero Snapping /// SizeFrame = 0x40000, /// /// WS_SYSMENU - Trigger the creation of the system menu /// SysMenu = 0x80000, } /// /// Track Popup Menu Flags /// /// private enum TPM { /// /// TPM_LEFTALIGN /// LeftAlign = 0x0000, /// /// TPM_RETURNCMD /// ReturnCommand = 0x0100, } #endregion #region Constants // Form Constants private const int BORDER_WIDTH = 7; private const int STATUS_BAR_BUTTON_WIDTH = 24; private const int STATUS_BAR_HEIGHT_DEFAULT = 24; private const int ICON_SIZE = 24; private const int PADDING_MINIMUM = 3; private const int TITLE_LEFT_PADDING = 72; private const int ACTION_BAR_PADDING = 16; private const int ACTION_BAR_HEIGHT_DEFAULT = 40; #endregion #region Private Fields private readonly Cursor[] _resizeCursors = { Cursors.SizeNESW, Cursors.SizeWE, Cursors.SizeNWSE, Cursors.SizeWE, Cursors.SizeNS }; private ResizeDirection _resizeDir; private ButtonState _buttonState = ButtonState.None; private FormStyles _formStyle; private Rectangle _minButtonBounds => new(ClientSize.Width - (3 * STATUS_BAR_BUTTON_WIDTH), ClientRectangle.Y, STATUS_BAR_BUTTON_WIDTH, STATUS_BAR_HEIGHT); private Rectangle _maxButtonBounds => new(ClientSize.Width - (2 * STATUS_BAR_BUTTON_WIDTH), ClientRectangle.Y, STATUS_BAR_BUTTON_WIDTH, STATUS_BAR_HEIGHT); private Rectangle _xButtonBounds => new(ClientSize.Width - STATUS_BAR_BUTTON_WIDTH, ClientRectangle.Y, STATUS_BAR_BUTTON_WIDTH, STATUS_BAR_HEIGHT); private Rectangle _actionBarBounds => new(ClientRectangle.X, ClientRectangle.Y + STATUS_BAR_HEIGHT, ClientSize.Width, ACTION_BAR_HEIGHT); private Rectangle _drawerButtonBounds => new(ClientRectangle.X + (SkinManager.FORM_PADDING / 2) + 3, STATUS_BAR_HEIGHT + (ACTION_BAR_HEIGHT / 2) - (ACTION_BAR_HEIGHT_DEFAULT / 2), ACTION_BAR_HEIGHT_DEFAULT, ACTION_BAR_HEIGHT_DEFAULT); private Rectangle _statusBarBounds => new(ClientRectangle.X, ClientRectangle.Y, ClientSize.Width, STATUS_BAR_HEIGHT); private Rectangle _drawerIconRect; private bool Maximized { get => WindowState == FormWindowState.Maximized; set { if (!MaximizeBox || !ControlBox) { return; } if (value) { WindowState = FormWindowState.Maximized; } else { WindowState = FormWindowState.Normal; } } } private Point _animationSource; private Padding originalPadding; private Form drawerOverlay = new(); private MaterialDrawerForm drawerForm = new(); // Drawer overlay and speed improvements private bool _drawerShowIconsWhenHidden; private bool _drawerAutoHide; private bool _drawerAutoShow; private bool _drawerIsOpen; private bool _drawerUseColors; private bool _drawerHighlightWithAccent; private bool _backgroundWithAccent; private MaterialDrawer drawerControl = new(); private AnimationManager _drawerShowHideAnimManager; private readonly AnimationManager _clickAnimManager; private int STATUS_BAR_HEIGHT = 24; private int ACTION_BAR_HEIGHT = 40; #endregion public MaterialForm() { DrawerWidth = 200; DrawerIsOpen = false; DrawerAutoHide = true; DrawerAutoShow = false; DrawerIndicatorWidth = 0; DrawerHighlightWithAccent = true; DrawerShowIconsWhenHidden = false; DrawerBackgroundWithAccent = false; Sizable = true; DoubleBuffered = true; FormBorderStyle = FormBorderStyle.None; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); FormStyle = FormStyles.ActionBar_40; //Keep space for resize by mouse Padding = new Padding(PADDING_MINIMUM, STATUS_BAR_HEIGHT + ACTION_BAR_HEIGHT, PADDING_MINIMUM, PADDING_MINIMUM); _clickAnimManager = new AnimationManager() { AnimationType = AnimationType.EaseOut, Increment = 0.04 }; _clickAnimManager.OnAnimationProgress += sender => Invalidate(); // Drawer Shown += (sender, e) => { if (DesignMode || IsDisposed) { return; } AddDrawerOverlayForm(); }; } #region Private Methods protected void AddDrawerOverlayForm() { if (DrawerTabControl == null) { return; } if (DrawerHideTabName.Any()) { int countHideTab = 0; foreach (System.Windows.Forms.TabPage TP in DrawerTabControl.TabPages) { if (DrawerHideTabName.Contains(TP.Name)) { countHideTab++; } } if (countHideTab >= DrawerTabControl.TabCount) { return; } } // Form opacity fade animation; _drawerShowHideAnimManager = new AnimationManager { AnimationType = AnimationType.EaseInOut, Increment = 0.04 }; _drawerShowHideAnimManager.OnAnimationProgress += (sender) => { drawerOverlay.Opacity = (float)(_drawerShowHideAnimManager.GetProgress() * 0.55f); }; int H = ClientSize.Height - _statusBarBounds.Height - _actionBarBounds.Height; int Y = PointToScreen(Point.Empty).Y + _statusBarBounds.Height + _actionBarBounds.Height; // Overlay Form definitions drawerOverlay.BackColor = Color.Black; drawerOverlay.Opacity = 0; drawerOverlay.MinimizeBox = false; drawerOverlay.MaximizeBox = false; drawerOverlay.Text = ""; drawerOverlay.ShowIcon = false; drawerOverlay.ControlBox = false; drawerOverlay.FormBorderStyle = FormBorderStyle.None; drawerOverlay.Visible = true; drawerOverlay.Size = new Size(ClientSize.Width, H); drawerOverlay.Location = new Point(PointToScreen(Point.Empty).X, Y); drawerOverlay.ShowInTaskbar = false; drawerOverlay.Owner = this; drawerOverlay.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; // Drawer Form definitions drawerForm.BackColor = Color.LimeGreen; drawerForm.TransparencyKey = Color.LimeGreen; drawerForm.MinimizeBox = false; drawerForm.MaximizeBox = false; drawerForm.Text = ""; drawerForm.ShowIcon = false; drawerForm.ControlBox = false; drawerForm.FormBorderStyle = FormBorderStyle.None; drawerForm.Visible = true; drawerForm.Size = new Size(DrawerWidth, H); drawerForm.Location = new Point(PointToScreen(Point.Empty).X, Y); drawerForm.ShowInTaskbar = false; drawerForm.Owner = this; //drawerOverlay drawerForm.TopMost = TopMost; drawerForm.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; // Add drawer to overlay form drawerForm.Controls.Add(drawerControl); drawerControl.Location = new Point(0, 0); drawerControl.Size = new Size(DrawerWidth, H); drawerControl.Anchor = AnchorStyles.Top | AnchorStyles.Bottom; drawerControl.BaseTabControl = DrawerTabControl; drawerControl.DrawerHideTabName = DrawerHideTabName; drawerControl.DrawerNonClickTabPage = DrawerNonClickTabPage; drawerControl.ShowIconsWhenHidden = true; // Init Options drawerControl.IsOpen = DrawerIsOpen; drawerControl.ShowIconsWhenHidden = DrawerShowIconsWhenHidden; drawerControl.AutoHide = DrawerAutoHide; drawerControl.AutoShow = DrawerAutoShow; drawerControl.IndicatorWidth = DrawerIndicatorWidth; drawerControl.HighlightWithAccent = DrawerHighlightWithAccent; drawerControl.BackgroundWithAccent = DrawerBackgroundWithAccent; // Changing colors or theme SkinManager.ThemeChanged += sender => { drawerForm.Refresh(); }; SkinManager.ColorSchemeChanged += sender => { drawerForm.Refresh(); }; // Visible, Resize and move events VisibleChanged += (sender, e) => { drawerForm.Visible = Visible; drawerOverlay.Visible = Visible; }; Resize += (sender, e) => { H = ClientSize.Height - _statusBarBounds.Height - _actionBarBounds.Height; drawerForm.Size = new Size(DrawerWidth, H); drawerOverlay.Size = new Size(ClientSize.Width, H); }; Move += (sender, e) => { Point pos = new(PointToScreen(Point.Empty).X, PointToScreen(Point.Empty).Y + _statusBarBounds.Height + _actionBarBounds.Height); drawerForm.Location = pos; drawerOverlay.Location = pos; }; // Close when click outside menu drawerOverlay.Click += (sender, e) => { drawerControl.Hide(); }; //Resize form when mouse over drawer drawerControl.MouseDown += (sender, e) => { ResizeForm(_resizeDir); }; // Animation and visibility drawerControl.DrawerBeginOpen += (sender) => { _drawerShowHideAnimManager.StartNewAnimation(AnimationDirection.In); }; drawerControl.DrawerBeginClose += (sender) => { _drawerShowHideAnimManager.StartNewAnimation(AnimationDirection.Out); }; drawerControl.CursorUpdate += (sender, drawerCursor) => { if (Sizable && !Maximized) { if (drawerCursor == Cursors.SizeNESW) { _resizeDir = ResizeDirection.BottomLeft; } else if (drawerCursor == Cursors.SizeWE) { _resizeDir = ResizeDirection.Left; } else if (drawerCursor == Cursors.SizeNS) { _resizeDir = ResizeDirection.Bottom; } else { _resizeDir = ResizeDirection.None; } } else { _resizeDir = ResizeDirection.None; } Cursor = drawerCursor; }; // Form Padding corrections if (Padding.Top < (_statusBarBounds.Height + _actionBarBounds.Height)) { Padding = new Padding(Padding.Left, _statusBarBounds.Height + _actionBarBounds.Height, Padding.Right, Padding.Bottom); } originalPadding = Padding; drawerControl.DrawerShowIconsWhenHiddenChanged += FixFormPadding; FixFormPadding(this); // Fix Closing the Drawer or Overlay form with Alt+F4 not exiting the app drawerOverlay.FormClosed += TerminateOnClose; drawerForm.FormClosed += TerminateOnClose; drawerForm.Attach(drawerControl); } private void TerminateOnClose(object sender, FormClosedEventArgs e) { Application.Exit(); } private void FixFormPadding(object sender) { if (drawerControl.ShowIconsWhenHidden) { Padding = new Padding(Padding.Left < drawerControl.MinWidth ? drawerControl.MinWidth : Padding.Left, originalPadding.Top, originalPadding.Right, originalPadding.Bottom); } else { Padding = new Padding(PADDING_MINIMUM, originalPadding.Top, originalPadding.Right, originalPadding.Bottom); } } private void UpdateButtons(MouseButtons button, Point location, bool up = false) { if (DesignMode) { return; } ButtonState oldState = _buttonState; bool showMin = MinimizeBox && ControlBox; bool showMax = MaximizeBox && ControlBox; if (button == MouseButtons.Left && !up) { if (showMin && !showMax && _maxButtonBounds.Contains(location)) { _buttonState = ButtonState.MinDown; } else if (showMin && showMax && _minButtonBounds.Contains(location)) { _buttonState = ButtonState.MinDown; } else if (showMax && _maxButtonBounds.Contains(location)) { _buttonState = ButtonState.MaxDown; } else if (ControlBox && _xButtonBounds.Contains(location)) { _buttonState = ButtonState.XDown; } else if (_drawerButtonBounds.Contains(location)) { _buttonState = ButtonState.DrawerDown; } else { _buttonState = ButtonState.None; } } else { if (showMin && !showMax && _maxButtonBounds.Contains(location)) { _buttonState = ButtonState.MinOver; if (oldState == ButtonState.MinDown && up) { WindowState = FormWindowState.Minimized; } } else if (showMin && showMax && _minButtonBounds.Contains(location)) { _buttonState = ButtonState.MinOver; if (oldState == ButtonState.MinDown && up) { WindowState = FormWindowState.Minimized; } } else if (showMax && _maxButtonBounds.Contains(location)) { _buttonState = ButtonState.MaxOver; if (oldState == ButtonState.MaxDown && up) { Maximized = !Maximized; } } else if (ControlBox && _xButtonBounds.Contains(location)) { _buttonState = ButtonState.XOver; if (oldState == ButtonState.XDown && up) { Close(); } } else if (_drawerButtonBounds.Contains(location)) { _buttonState = ButtonState.DrawerOver; } else { _buttonState = ButtonState.None; } } if (oldState != _buttonState) { Invalidate(); } } private void ResizeForm(ResizeDirection direction) { if (DesignMode) { return; } int dir = -1; switch (direction) { case ResizeDirection.BottomLeft: dir = (int)HT.BottomLeft; Cursor = Cursors.SizeNESW; break; case ResizeDirection.Left: dir = (int)HT.Left; Cursor = Cursors.SizeWE; break; case ResizeDirection.Right: dir = (int)HT.Right; break; case ResizeDirection.BottomRight: dir = (int)HT.BottomRight; break; case ResizeDirection.Bottom: dir = (int)HT.Bottom; break; case ResizeDirection.Top: dir = (int)HT.Top; break; case ResizeDirection.TopLeft: dir = (int)HT.TopLeft; break; case ResizeDirection.TopRight: dir = (int)HT.TopRight; break; } ReleaseCapture(); if (dir != -1) { SendMessage(Handle, (int)WM.NonClientLeftButtonDown, dir, 0); } } private void RecalculateFormBoundaries() { switch (_formStyle) { case FormStyles.StatusAndActionBar_None: ACTION_BAR_HEIGHT = 0; STATUS_BAR_HEIGHT = 0; break; case FormStyles.ActionBar_None: ACTION_BAR_HEIGHT = 0; STATUS_BAR_HEIGHT = STATUS_BAR_HEIGHT_DEFAULT; break; case FormStyles.ActionBar_40: ACTION_BAR_HEIGHT = ACTION_BAR_HEIGHT_DEFAULT; STATUS_BAR_HEIGHT = STATUS_BAR_HEIGHT_DEFAULT; break; case FormStyles.ActionBar_48: ACTION_BAR_HEIGHT = 48; STATUS_BAR_HEIGHT = STATUS_BAR_HEIGHT_DEFAULT; break; case FormStyles.ActionBar_56: ACTION_BAR_HEIGHT = 56; STATUS_BAR_HEIGHT = STATUS_BAR_HEIGHT_DEFAULT; break; case FormStyles.ActionBar_64: ACTION_BAR_HEIGHT = 64; STATUS_BAR_HEIGHT = STATUS_BAR_HEIGHT_DEFAULT; break; default: ACTION_BAR_HEIGHT = ACTION_BAR_HEIGHT_DEFAULT; STATUS_BAR_HEIGHT = STATUS_BAR_HEIGHT_DEFAULT; break; } Padding = new Padding(_drawerShowIconsWhenHidden ? drawerControl.MinWidth : PADDING_MINIMUM, STATUS_BAR_HEIGHT + ACTION_BAR_HEIGHT, Padding.Right, Padding.Bottom); originalPadding = Padding; if (DrawerTabControl != null) { int height = ClientSize.Height - (STATUS_BAR_HEIGHT + ACTION_BAR_HEIGHT); Point location = Point.Add(Location, new Size(0, STATUS_BAR_HEIGHT + ACTION_BAR_HEIGHT)); drawerOverlay.Size = new Size(ClientSize.Width, height); drawerOverlay.Location = location; drawerForm.Size = new Size(DrawerWidth, height); drawerForm.Location = location; } Invalidate(); } #endregion #region WinForms Methods protected override CreateParams CreateParams { get { CreateParams par = base.CreateParams; par.Style |= (int)WS.MinimizeBox | (int)WS.SysMenu; return par; } } protected override void OnCreateControl() { base.OnCreateControl(); // Sets the Window Style for having a Size Frame after the form is created // This prevents unexpected sizing while still allowing for Aero Snapping long flags = GetWindowLongPtr(Handle, -16).ToInt64(); SetWindowLongPtr(Handle, -16, (IntPtr)(flags | (int)WS.SizeFrame)); } protected override void WndProc(ref Message m) { WM message = (WM)m.Msg; // Prevent the base class from receiving the message if (message == WM.NonClientCalcSize) { return; } // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate?redirectedfrom=MSDN#parameters // "If this parameter is set to -1, DefWindowProc does not repaint the nonclient area to reflect the state change." if (message == WM.NonClientActivate) { m.Result = new IntPtr(-1); return; } base.WndProc(ref m); if (DesignMode || IsDisposed) { return; } Point cursorPos = PointToClient(Cursor.Position); bool isOverCaption = (_statusBarBounds.Contains(cursorPos) || _actionBarBounds.Contains(cursorPos)) && !(_minButtonBounds.Contains(cursorPos) || _maxButtonBounds.Contains(cursorPos) || _xButtonBounds.Contains(cursorPos)); // Drawer if (DrawerTabControl != null && (message == WM.LeftButtonDown || message == WM.LeftButtonDoubleClick) && _drawerIconRect.Contains(cursorPos)) { drawerControl.Toggle(); _clickAnimManager.SetProgress(0); _clickAnimManager.StartNewAnimation(AnimationDirection.In); _animationSource = cursorPos; } // Form blocked in minimized state (privilege mode) else if (WindowState == FormWindowState.Minimized && message == WM.ActivateApp) { drawerOverlay.Invalidate(); drawerOverlay.Update(); drawerForm.Invalidate(); drawerForm.Update(); this.Invalidate(); this.Update(); } // Double click to maximize else if (message == WM.LeftButtonDoubleClick && isOverCaption) { Maximized = !Maximized; } // Treat the Caption as if it was Non-Client else if (message == WM.LeftButtonDown && isOverCaption) { ReleaseCapture(); SendMessage(Handle, (int)WM.NonClientLeftButtonDown, (int)HT.Caption, 0); } // Default context menu else if (message == WM.RightButtonDown) { if (_statusBarBounds.Contains(cursorPos) && !_minButtonBounds.Contains(cursorPos) && !_maxButtonBounds.Contains(cursorPos) && !_xButtonBounds.Contains(cursorPos)) { // Temporary disable user defined ContextMenuStrip ContextMenuStrip user_cms = base.ContextMenuStrip; base.ContextMenuStrip = null; // Show default system menu when right clicking titlebar int id = TrackPopupMenuEx(GetSystemMenu(Handle, false), (int)TPM.LeftAlign | (int)TPM.ReturnCommand, Cursor.Position.X, Cursor.Position.Y, Handle, IntPtr.Zero); // Pass the command as a WM_SYSCOMMAND message SendMessage(Handle, (int)WM.SystemCommand, id, 0); // restore user defined ContextMenuStrip base.ContextMenuStrip = user_cms; } } } protected override void OnMove(EventArgs e) { // Empty Point ensures the screen maximizes to the top left of the current screen MaximizedBounds = new Rectangle(Point.Empty, Screen.GetWorkingArea(Location).Size); base.OnMove(e); } protected override void OnMouseDown(MouseEventArgs e) { if (DesignMode) { return; } UpdateButtons(e.Button, e.Location); if (e.Button == MouseButtons.Left && !Maximized && _resizeCursors.Contains(Cursor)) { ResizeForm(_resizeDir); } base.OnMouseDown(e); } protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); Cursor = Cursors.Default; } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); if (DesignMode) { return; } _buttonState = ButtonState.None; _resizeDir = ResizeDirection.None; //Only reset the cursor when needed if (_resizeCursors.Contains(Cursor)) { Cursor = Cursors.Default; } Invalidate(); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (DesignMode) { return; } Point coords = e.Location; UpdateButtons(e.Button, coords); if (DrawerTabControl != null && _drawerIconRect.Contains(e.Location)) { Cursor = DrawerHamburgerCursor; } else { Cursor = Cursors.Default; } if (!Sizable) { return; } //True if the mouse is hovering over a child control bool isChildUnderMouse = GetChildAtPoint(coords) != null; if (!isChildUnderMouse && !Maximized && coords.Y < BORDER_WIDTH && coords.X > BORDER_WIDTH && coords.X < ClientSize.Width - BORDER_WIDTH) { _resizeDir = ResizeDirection.Top; Cursor = Cursors.SizeNS; } else if (!isChildUnderMouse && !Maximized && coords.X <= BORDER_WIDTH && coords.Y < BORDER_WIDTH) { _resizeDir = ResizeDirection.TopLeft; Cursor = Cursors.SizeNWSE; } else if (!isChildUnderMouse && !Maximized && coords.X >= ClientSize.Width - BORDER_WIDTH && coords.Y < BORDER_WIDTH) { _resizeDir = ResizeDirection.TopRight; Cursor = Cursors.SizeNESW; } else if (!isChildUnderMouse && !Maximized && coords.X <= BORDER_WIDTH && coords.Y >= ClientSize.Height - BORDER_WIDTH) { _resizeDir = ResizeDirection.BottomLeft; Cursor = Cursors.SizeNESW; } else if ((!isChildUnderMouse || DrawerTabControl != null) && !Maximized && coords.X <= BORDER_WIDTH) { _resizeDir = ResizeDirection.Left; Cursor = Cursors.SizeWE; } else if (!isChildUnderMouse && !Maximized && coords.X >= ClientSize.Width - BORDER_WIDTH && coords.Y >= ClientSize.Height - BORDER_WIDTH) { _resizeDir = ResizeDirection.BottomRight; Cursor = Cursors.SizeNWSE; } else if (!isChildUnderMouse && !Maximized && coords.X >= ClientSize.Width - BORDER_WIDTH) { _resizeDir = ResizeDirection.Right; Cursor = Cursors.SizeWE; } else if (!isChildUnderMouse && !Maximized && coords.Y >= ClientSize.Height - BORDER_WIDTH) { _resizeDir = ResizeDirection.Bottom; Cursor = Cursors.SizeNS; } else { _resizeDir = ResizeDirection.None; //Only reset the cursor when needed, this prevents it from flickering when a child control changes the cursor to its own needs if (_resizeCursors.Contains(Cursor)) { Cursor = Cursors.Default; } } } protected override void OnMouseUp(MouseEventArgs e) { if (DesignMode) { return; } UpdateButtons(e.Button, e.Location, true); base.OnMouseUp(e); ReleaseCapture(); } protected override void OnPaint(PaintEventArgs e) { Brush hoverBrush = SkinManager.BackgroundHoverBrush; Brush downBrush = SkinManager.BackgroundFocusBrush; Graphics g = e.Graphics; g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; g.Clear(SkinManager.BackdropColor); //Draw border using (Pen borderPen = new(SkinManager.DividersColor, 1)) { g.DrawLine(borderPen, new Point(0, _actionBarBounds.Bottom), new Point(0, ClientSize.Height - 2)); g.DrawLine(borderPen, new Point(ClientSize.Width - 1, _actionBarBounds.Bottom), new Point(ClientSize.Width - 1, ClientSize.Height - 2)); g.DrawLine(borderPen, new Point(0, ClientSize.Height - 1), new Point(ClientSize.Width - 1, ClientSize.Height - 1)); } if (_formStyle != FormStyles.StatusAndActionBar_None) { if (ControlBox) { g.FillRectangle(SkinManager.ColorScheme.DarkPrimaryBrush, _statusBarBounds); g.FillRectangle(SkinManager.ColorScheme.PrimaryBrush, _actionBarBounds); } // Determine whether or not we even should be drawing the buttons. bool showMin = MinimizeBox && ControlBox; bool showMax = MaximizeBox && ControlBox; // When MaximizeButton == false, the minimize button will be painted in its place if (_buttonState == ButtonState.MinOver && showMin) { g.FillRectangle(hoverBrush, showMax ? _minButtonBounds : _maxButtonBounds); } if (_buttonState == ButtonState.MinDown && showMin) { g.FillRectangle(downBrush, showMax ? _minButtonBounds : _maxButtonBounds); } if (_buttonState == ButtonState.MaxOver && showMax) { g.FillRectangle(hoverBrush, _maxButtonBounds); } if (_buttonState == ButtonState.MaxDown && showMax) { g.FillRectangle(downBrush, _maxButtonBounds); } if (_buttonState == ButtonState.XOver && ControlBox) { g.FillRectangle(SkinManager.BackgroundHoverRedBrush, _xButtonBounds); } if (_buttonState == ButtonState.XDown && ControlBox) { g.FillRectangle(SkinManager.BackgroundDownRedBrush, _xButtonBounds); } using Pen formButtonsPen = new(SkinManager.ColorScheme.TextColor, 2); // Minimize button. if (showMin) { int x = showMax ? _minButtonBounds.X : _maxButtonBounds.X; int y = showMax ? _minButtonBounds.Y : _maxButtonBounds.Y; g.DrawLine( formButtonsPen, x + (int)(_minButtonBounds.Width * 0.33), y + (int)(_minButtonBounds.Height * 0.66), x + (int)(_minButtonBounds.Width * 0.66), y + (int)(_minButtonBounds.Height * 0.66) ); } // Maximize button if (showMax) { if (WindowState != FormWindowState.Maximized) { g.DrawRectangle( formButtonsPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.33), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.36), (int)(_maxButtonBounds.Width * 0.39), (int)(_maxButtonBounds.Height * 0.31) ); } else { // Change position of square g.DrawRectangle( formButtonsPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.30), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.42), (int)(_maxButtonBounds.Width * 0.40), (int)(_maxButtonBounds.Height * 0.32) ); // Draw lines for background square g.DrawLine(formButtonsPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.42), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.30), _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.42), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.38) ); g.DrawLine(formButtonsPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.40), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.30), _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.86), _maxButtonBounds.Y + (int)(_maxButtonBounds.Width * 0.30) ); g.DrawLine(formButtonsPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.82), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.28), _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.82), _maxButtonBounds.Y + (int)(_maxButtonBounds.Width * 0.64) ); g.DrawLine(formButtonsPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.70), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.62), _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.84), _maxButtonBounds.Y + (int)(_maxButtonBounds.Width * 0.62) ); } } // Close button if (ControlBox) { g.DrawLine( formButtonsPen, _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.33), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.33), _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.66), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.66) ); g.DrawLine( formButtonsPen, _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.66), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.33), _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.33), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.66)); } } // Drawer Icon if (DrawerTabControl != null && _formStyle != FormStyles.ActionBar_None && _formStyle != FormStyles.StatusAndActionBar_None) { if (_buttonState == ButtonState.DrawerOver) { g.FillRectangle(hoverBrush, _drawerButtonBounds); } if (_buttonState == ButtonState.DrawerDown) { g.FillRectangle(downBrush, _drawerButtonBounds); } _drawerIconRect = new Rectangle(SkinManager.FORM_PADDING / 2, STATUS_BAR_HEIGHT, ACTION_BAR_HEIGHT_DEFAULT, ACTION_BAR_HEIGHT); // Ripple if (_clickAnimManager.IsAnimating()) { double clickAnimProgress = _clickAnimManager.GetProgress(); SolidBrush rippleBrush = new(Color.FromArgb((int)(51 - (clickAnimProgress * 50)), Color.White)); int rippleSize = (int)(clickAnimProgress * _drawerIconRect.Width * 1.75); g.SetClip(_drawerIconRect); g.FillEllipse(rippleBrush, new Rectangle(_animationSource.X - (rippleSize / 2), _animationSource.Y - (rippleSize / 2), rippleSize, rippleSize)); g.ResetClip(); rippleBrush.Dispose(); } using Pen formButtonsPen = new(SkinManager.ColorScheme.TextColor, 2); // Middle line g.DrawLine( formButtonsPen, _drawerIconRect.X + (int)SkinManager.FORM_PADDING, _drawerIconRect.Y + (int)(ACTION_BAR_HEIGHT / 2), _drawerIconRect.X + (int)SkinManager.FORM_PADDING + 18, _drawerIconRect.Y + (int)(ACTION_BAR_HEIGHT / 2)); // Bottom line g.DrawLine( formButtonsPen, _drawerIconRect.X + (int)SkinManager.FORM_PADDING, _drawerIconRect.Y + (int)(ACTION_BAR_HEIGHT / 2) - 6, _drawerIconRect.X + (int)SkinManager.FORM_PADDING + 18, _drawerIconRect.Y + (int)(ACTION_BAR_HEIGHT / 2) - 6); // Top line g.DrawLine( formButtonsPen, _drawerIconRect.X + (int)SkinManager.FORM_PADDING, _drawerIconRect.Y + (int)(ACTION_BAR_HEIGHT / 2) + 6, _drawerIconRect.X + (int)SkinManager.FORM_PADDING + 18, _drawerIconRect.Y + (int)(ACTION_BAR_HEIGHT / 2) + 6); } if (ControlBox == true && _formStyle != FormStyles.ActionBar_None && _formStyle != FormStyles.StatusAndActionBar_None) { //Form title using MaterialNativeTextRenderer NativeText = new(g); Rectangle textLocation = new(DrawerTabControl != null ? TITLE_LEFT_PADDING : TITLE_LEFT_PADDING - (ICON_SIZE + (ACTION_BAR_PADDING * 2)), STATUS_BAR_HEIGHT, ClientSize.Width, ACTION_BAR_HEIGHT); NativeText.DrawTransparentText(Text, SkinManager.GetLogFontByType(MaterialSkinManager.FontType.H6), SkinManager.ColorScheme.TextColor, textLocation.Location, textLocation.Size, MaterialNativeTextRenderer.TextAlignFlags.Left | MaterialNativeTextRenderer.TextAlignFlags.Middle); } } #endregion #region Low Level Windows Methods /// /// Provides a single method to call either the 32-bit or 64-bit method based on the size of an for getting the /// Window Style flags.
/// GetWindowLongPtr ///
private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex) { if (IntPtr.Size == 8) { return GetWindowLongPtr64(hWnd, nIndex); } else { return GetWindowLong(hWnd, nIndex); } } /// /// Provides a single method to call either the 32-bit or 64-bit method based on the size of an for setting the /// Window Style flags.
/// SetWindowLongPtr ///
private static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong) { if (IntPtr.Size == 8) { return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); } else { return SetWindowLong(hWnd, nIndex, dwNewLong.ToInt32()); } } [DllImport("user32.dll", EntryPoint = "GetWindowLong")] private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")] private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex); [DllImport("user32.dll", EntryPoint = "SetWindowLong")] private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")] private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong); [DllImport("user32.dll")] private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [DllImport("user32.dll")] private static extern bool ReleaseCapture(); [DllImport("user32.dll")] private static extern int TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm); [DllImport("user32.dll")] private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); #endregion } #endregion }