Hi.
I’m developing a plugin to apply a “Modern style custom title bar” to mission planner.
It is working well(although it needs modification), but I’m curious about the way to safely quit mission planner from the custom title bar quit button.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MissionPlanner.Utilities;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading.Tasks;
using MissionPlanner;
using System.Drawing; // Point Class
using System.Runtime.InteropServices; // dll import(Resizing Window)
namespace MissionPlannerUICustomizer
{
public class MissionPlannerUICustomizer : MissionPlanner.Plugin.Plugin
{
private Panel titleBar;
private Label titleLabel;
private Button closeButtonOnTitle;
private Button minimizeButtonOnTitle;
private Button maximizeButtonOnTitle;
// Because we set FormBorderStyle = None, custom dragging event needed.
// Transparent Panel for dragging window(mouse down, move event)
private Panel leftEdge;
private Panel rightEdge;
private Panel topEdge;
private Panel bottomEdge;
private Point mouseOffsetOnTitle;
private bool isMouseDownOnTitle = false;
// Win32 API for custom resizing event
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
// Win32 API for custom resizing event
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
// Costants for custom window resizing function
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_SIZE = 0xF000;
private const int RESIZE_LEFT = 1;
private const int RESIZE_RIGHT = 2;
private const int RESIZE_TOP = 3;
private const int RESIZE_BOTTOM = 6;
public override string Name
{
get { return "MissionPlannerUICustomizer"; }
}
public override string Version
{
get { return "2025.06. Developing."; }
}
public override string Author
{
get { return "Me"; }
}
public override bool Init()
{
MainV2.instance.FormBorderStyle = FormBorderStyle.None; // For custom title bar
MainV2.instance.StartPosition = FormStartPosition.Manual; // Window jumps out on the screen
MainV2.instance.Location = new Point(0, 0);
// MainV2.instance.Size = new Size(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height);
// MainV2.instance.Padding = new Padding(3); // Sometimes putting padding enables dragging.
return true;
}
public override bool Loaded()
{
Console.WriteLine("Mission Planner UI Customizer Plugin Loaded.");
titleBar = new Panel();
titleBar.Dock = DockStyle.Top;
titleBar.Height = 30;
titleBar.MouseDown += titleBarMouseDown;
titleBar.MouseMove += titleBarMouseMove;
titleBar.MouseUp += titleBarMouseUp;
// We have to implement maximize and minimize function by ourselves because we set FormBorderStyle = None
titleBar.MouseDoubleClick += titleBarDoubleClick;
MainV2.instance.Controls.Add(titleBar);
closeButtonOnTitle = new Button();
closeButtonOnTitle.FlatStyle = FlatStyle.Flat;
closeButtonOnTitle.FlatAppearance.BorderSize = 0;
closeButtonOnTitle.Size = new Size(30, 30);
closeButtonOnTitle.Location = new Point(titleBar.Width - closeButtonOnTitle.Width, 0);
closeButtonOnTitle.Anchor = AnchorStyles.Top | AnchorStyles.Right;
// closeButtonOnTitle.Anchor = AnchorStyles.Right;
closeButtonOnTitle.Text = "r"; // "x" in Webdings
closeButtonOnTitle.Font = new Font("Webdings", 10, FontStyle.Bold);
closeButtonOnTitle.Click += (s, e) =>
{
MainV2.instance.Close(); // ????
};
titleBar.Controls.Add(closeButtonOnTitle);
minimizeButtonOnTitle = new Button();
minimizeButtonOnTitle.FlatStyle = FlatStyle.Flat;
minimizeButtonOnTitle.FlatAppearance.BorderSize = 0;
minimizeButtonOnTitle.Size = new Size(30, 30);
minimizeButtonOnTitle.Location = new Point(titleBar.Width - 90, 0);
minimizeButtonOnTitle.Anchor = AnchorStyles.Top | AnchorStyles.Right;
minimizeButtonOnTitle.Text = "0"; // Minimize button on "webdings"
minimizeButtonOnTitle.Font = new Font("Webdings", 10, FontStyle.Bold);
minimizeButtonOnTitle.Click += (s, e) =>
{
MainV2.instance.WindowState = FormWindowState.Minimized;
};
titleBar.Controls.Add(minimizeButtonOnTitle);
maximizeButtonOnTitle = new Button();
maximizeButtonOnTitle.FlatStyle = FlatStyle.Flat;
maximizeButtonOnTitle.FlatAppearance.BorderSize = 0;
maximizeButtonOnTitle.Size = new Size(30, 30);
maximizeButtonOnTitle.Location = new Point(titleBar.Width - 60, 0);
maximizeButtonOnTitle.Anchor = AnchorStyles.Top | AnchorStyles.Right;
maximizeButtonOnTitle.Text = "1"; // maximize button in webdings
maximizeButtonOnTitle.Font = new Font("Webdings", 10, FontStyle.Bold);
maximizeButtonOnTitle.Click += (s, e) =>
{
if (MainV2.instance.Size == Screen.FromHandle(MainV2.instance.Handle).WorkingArea.Size)
{
MainV2.instance.WindowState = FormWindowState.Normal;
maximizeButtonOnTitle.Text = "1"; // Maximize icon
}
else
{
Rectangle workingArea = Screen.FromHandle(MainV2.instance.Handle).WorkingArea;
MainV2.instance.Location = workingArea.Location;
MainV2.instance.Size = workingArea.Size;
maximizeButtonOnTitle.Text = "2"; // Return to Normal Window Icon
}
};
titleBar.Controls.Add(maximizeButtonOnTitle);
titleLabel = new Label();
titleLabel.Text = "Custom Title Bar 20250630";
titleLabel.Font = new Font("Segoe UI", 10, FontStyle.Bold);
titleLabel.TextAlign = ContentAlignment.MiddleLeft;
titleLabel.AutoSize = false;
titleLabel.Dock = DockStyle.Left;
titleLabel.Width = titleBar.Width - 3 * closeButtonOnTitle.Width; // Calculation timing is important
Console.WriteLine($"title label width : {titleLabel.Width}");
titleBar.Controls.Add(titleLabel);
titleLabel.BringToFront(); // No Overlap
titleLabel.MouseDown += titleBarMouseDown;
titleLabel.MouseUp += titleBarMouseUp;
titleLabel.MouseMove += titleBarMouseMove;
titleLabel.MouseDoubleClick += titleBarDoubleClick;
int _BORDER_SIZE = 2;
// Draw borderlines for mission planner program
MainV2.instance.Paint += (s, e) =>
{
Color borderColor = Color.FromArgb(74, 83, 86);
ControlPaint.DrawBorder(e.Graphics, MainV2.instance.ClientRectangle,
borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid,
borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid,
borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid,
borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid);
};
// Transparent panel for resizing window
leftEdge = new Panel();
leftEdge.Width = _BORDER_SIZE;
leftEdge.Dock = DockStyle.Left;
leftEdge.BackColor = Color.Transparent;
leftEdge.Cursor = Cursors.SizeWE; // West & East
MainV2.instance.Controls.Add(leftEdge);
rightEdge = new Panel();
rightEdge.Width = _BORDER_SIZE;
rightEdge.Dock = DockStyle.Right;
rightEdge.BackColor = Color.Transparent;
rightEdge.Cursor = Cursors.SizeWE; // West & East
MainV2.instance.Controls.Add(rightEdge);
bottomEdge = new Panel();
bottomEdge.Height = _BORDER_SIZE;
bottomEdge.Dock = DockStyle.Bottom;
bottomEdge.BackColor = Color.Transparent;
bottomEdge.Cursor = Cursors.SizeNS; // North & South
MainV2.instance.Controls.Add(bottomEdge);
topEdge = new Panel();
topEdge.Height = _BORDER_SIZE;
topEdge.Dock = DockStyle.Top;
topEdge.BackColor = Color.Transparent;
topEdge.Cursor = Cursors.SizeNS; // North & South
MainV2.instance.Controls.Add(topEdge);
leftEdge.MouseDown += (s, e) =>
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_LEFT, 0);
}
};
rightEdge.MouseDown += (s, e) =>
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_RIGHT, 0);
}
};
topEdge.MouseDown += (s, e) =>
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_TOP, 0);
}
};
bottomEdge.MouseDown += (s, e) =>
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_BOTTOM, 0);
}
};
MainV2.instance.Resize += (s, e) =>
{
//closeButtonOnTitle.Location = new Point(titleBar.Width - closeButtonOnTitle.Width, 0);
//minimizeButtonOnTitle.Location = new Point(titleBar.Width - 90, 0);
//maximizeButtonOnTitle.Location = new Point(titleBar.Width - 60, 0);
titleLabel.Width = titleBar.Width - 3 * closeButtonOnTitle.Width;
};
ApplyCustomStyles();
this.loopratehz = 0;
return false;
}
// Implement dragging function
private void titleBarMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (MainV2.instance.Size == Screen.FromHandle(MainV2.instance.Handle).WorkingArea.Size)
{
MainV2.instance.WindowState = FormWindowState.Normal;
maximizeButtonOnTitle.Text = "1";
}
mouseOffsetOnTitle = new Point(-e.X, -e.Y);
isMouseDownOnTitle = true;
}
}
private void titleBarMouseMove(object sender, MouseEventArgs e)
{
if (isMouseDownOnTitle)
{
Point mousePos = Control.MousePosition;
mousePos.Offset(mouseOffsetOnTitle.X, mouseOffsetOnTitle.Y);
MainV2.instance.Location = mousePos;
}
}
private void titleBarMouseUp(object sender, MouseEventArgs e)
{
isMouseDownOnTitle = false;
}
private void titleBarDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (MainV2.instance.Size == Screen.FromHandle(MainV2.instance.Handle).WorkingArea.Size)
{
MainV2.instance.WindowState = FormWindowState.Normal;
maximizeButtonOnTitle.Text = "1";
}
else
{
// MainV2.instance.WindowState = FormWindowState.Maximized;
Rectangle workingArea = Screen.FromHandle(MainV2.instance.Handle).WorkingArea;
MainV2.instance.Location = workingArea.Location;
MainV2.instance.Size = workingArea.Size;
maximizeButtonOnTitle.Text = "2";
}
}
}
private void ApplyCustomStyles()
{
titleBar.ForeColor = Color.FromArgb(70, 70, 70);
titleBar.BackColor = Color.FromArgb(74, 83, 86);
titleLabel.ForeColor = Color.DarkGray;
titleLabel.BackColor = Color.Transparent;
closeButtonOnTitle.ForeColor = Color.DarkGray;
closeButtonOnTitle.BackColor = Color.FromArgb(74, 83, 86);
minimizeButtonOnTitle.ForeColor = Color.DarkGray;
minimizeButtonOnTitle.BackColor = Color.FromArgb(74, 83, 86);
maximizeButtonOnTitle.ForeColor = Color.DarkGray;
maximizeButtonOnTitle.BackColor = Color.FromArgb(74, 83, 86);
}
public override bool Exit()
{
return false;
}
} // end of plugin class
} // end of namespace MissionPlannerUICustomizer
This code contains following line :
closeButtonOnTitle.Click += (s, e) =>
{
MainV2.instance.Close(); // ????
};
This calls MainV2.instance.Close() directly when the custom close button is clicked.
I wonder this is correct way to quit mission planner.
There is Application.Quit() ,and other many ways to do this.
I tested turning on and off more than 50 times but there was no issue.
However, I’d really appreciate confirmation from an expert or someone familiar with Mission Planner’s internals