mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-24 04:41:10 +00:00
25b68aa7e2
Internal: replaced CollapsibleSplitter with CollapsibleSplitContainer.
388 lines
12 KiB
C#
388 lines
12 KiB
C#
#region ================== Copyright (c) 2015 MaxED
|
|
|
|
// Parts of the code are based on "Collapsible Splitter control in C#" by Furty
|
|
// http://www.codeproject.com/Articles/3025/Collapsible-Splitter-control-in-C
|
|
|
|
#endregion
|
|
|
|
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Reflection;
|
|
using System.Windows.Forms;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.Controls
|
|
{
|
|
[Designer("System.Windows.Forms.Design.SplitContainerDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
|
|
public class CollapsibleSplitContainer : SplitContainer, ISupportInitialize
|
|
{
|
|
#region ================== Private Properties
|
|
|
|
// Declare and define some base properties
|
|
private bool hot;
|
|
private bool collapsed;
|
|
private readonly Color hotcolor = CalculateColor(SystemColors.Highlight, SystemColors.Window, 70);
|
|
private Rectangle bounds;
|
|
|
|
// Storesome settings
|
|
private int storedpanel1minsize;
|
|
private int storedpanel2minsize;
|
|
private int storedsplitterdistance;
|
|
|
|
#endregion
|
|
|
|
#region ================== Public Properties
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public bool IsCollapsed
|
|
{
|
|
get { return collapsed; }
|
|
set { collapsed = value; ToggleSplitter(); }
|
|
}
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public int SplitPosition
|
|
{
|
|
get { return GetSplitPosition(); }
|
|
set { storedsplitterdistance = value; if(!IsCollapsed) ToggleSplitter(); }
|
|
}
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
private new int SplitterWidth
|
|
{
|
|
get { return base.SplitterWidth; }
|
|
set { base.SplitterWidth = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor
|
|
|
|
public CollapsibleSplitContainer()
|
|
{
|
|
// Register mouse events
|
|
this.Click += OnClick;
|
|
this.Resize += OnResize;
|
|
this.MouseLeave += OnMouseLeave;
|
|
this.MouseMove += OnMouseMove;
|
|
this.MouseUp += OnMouseUp;
|
|
|
|
//mxd. Set drawing style
|
|
const ControlStyles cs = ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer;
|
|
this.SetStyle(cs, true);
|
|
|
|
object[] args = new object[] { cs, true };
|
|
MethodInfo objMethodInfo = typeof(Control).GetMethod("SetStyle", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
objMethodInfo.Invoke(this.Panel1, args);
|
|
objMethodInfo.Invoke(this.Panel2, args);
|
|
|
|
// Force the width to 8px so that everything always draws correctly
|
|
this.SplitterWidth = 8;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Event Handlers
|
|
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
// if the hider control isn't hot, let the base resize action occur
|
|
if(!this.hot && this.Panel1.Visible && this.Panel2.Visible) base.OnMouseDown(e);
|
|
}
|
|
|
|
private void OnResize(object sender, EventArgs e)
|
|
{
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void OnMouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
// check to see if the mouse cursor position is within the bounds of our control
|
|
if(bounds.Contains(e.Location))
|
|
{
|
|
if(!this.hot)
|
|
{
|
|
this.hot = true;
|
|
this.Cursor = Cursors.Hand;
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(this.hot)
|
|
{
|
|
this.hot = false;
|
|
this.Invalidate();
|
|
}
|
|
|
|
this.Cursor = Cursors.Default;
|
|
}
|
|
}
|
|
|
|
private void OnMouseLeave(object sender, EventArgs e)
|
|
{
|
|
// ensure that the hot state is removed
|
|
this.hot = false;
|
|
this.Invalidate();
|
|
}
|
|
|
|
// User may've moved the splitter...
|
|
private void OnMouseUp(object sender, MouseEventArgs mouseEventArgs)
|
|
{
|
|
if(!collapsed) storedsplitterdistance = GetSplitPosition();
|
|
}
|
|
|
|
private void OnClick(object sender, EventArgs e)
|
|
{
|
|
if(FixedPanel != FixedPanel.None && hot)
|
|
{
|
|
collapsed = !collapsed;
|
|
ToggleSplitter();
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Paint
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
base.OnPaint(e);
|
|
if(FixedPanel == FixedPanel.None) return;
|
|
|
|
// find the rectangle for the splitter and paint it
|
|
Rectangle r = this.SplitterRectangle;
|
|
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), r);
|
|
|
|
Pen penlightlight = new Pen(SystemColors.ControlLightLight);
|
|
Pen pendark = new Pen(SystemColors.ControlDark);
|
|
SolidBrush brushdarkdark = new SolidBrush(SystemColors.ControlDarkDark);
|
|
|
|
// Check the docking style and create the control rectangle accordingly
|
|
if(this.Orientation == Orientation.Vertical)
|
|
{
|
|
// create a new rectangle in the vertical center of the splitter for our collapse control button
|
|
bounds = new Rectangle(r.X, r.Y + ((r.Height - 115) / 2), 8, 115);
|
|
|
|
// draw the background color for our control image
|
|
e.Graphics.FillRectangle(new SolidBrush(hot ? hotcolor : this.BackColor), new Rectangle(bounds.X + 1, bounds.Y, 6, 115));
|
|
|
|
// draw the top & bottom lines for our control image
|
|
e.Graphics.DrawLine(pendark, bounds.X + 1, bounds.Y, bounds.X + bounds.Width - 2, bounds.Y);
|
|
e.Graphics.DrawLine(pendark, bounds.X + 1, bounds.Y + bounds.Height, bounds.X + bounds.Width - 2, bounds.Y + bounds.Height);
|
|
|
|
if(this.Enabled)
|
|
{
|
|
// draw the arrows for our control image
|
|
// the ArrowPointArray is a point array that defines an arrow shaped polygon
|
|
e.Graphics.FillPolygon(brushdarkdark, ArrowPointArray(bounds.X + 2, bounds.Y + 3));
|
|
e.Graphics.FillPolygon(brushdarkdark, ArrowPointArray(bounds.X + 2, bounds.Y + bounds.Height - 9));
|
|
}
|
|
|
|
// draw the dots for our control image using a loop
|
|
int x = bounds.X + 3;
|
|
int y = bounds.Y + 14;
|
|
|
|
for(int i = 0; i < 30; i++)
|
|
{
|
|
// light dot
|
|
e.Graphics.DrawRectangle(penlightlight, x, y + 1 + (i * 3), 1, 1);
|
|
// dark dot
|
|
e.Graphics.DrawRectangle(pendark, x - 1, y + (i * 3), 1, 1);
|
|
i++;
|
|
// light dot
|
|
e.Graphics.DrawRectangle(penlightlight, x + 2, y + 1 + (i * 3), 1, 1);
|
|
// dark dot
|
|
e.Graphics.DrawRectangle(pendark, x + 1, y + (i * 3), 1, 1);
|
|
}
|
|
}
|
|
else // Should be Orientation.Horizontal
|
|
{
|
|
// create a new rectangle in the horizontal center of the splitter for our collapse control button
|
|
bounds = new Rectangle(r.X + ((r.Width - 115) / 2), r.Y, 115, 8);
|
|
|
|
// draw the background color for our control image
|
|
e.Graphics.FillRectangle(new SolidBrush(hot ? hotcolor : this.BackColor), new Rectangle(bounds.X, bounds.Y + 1, 115, 6));
|
|
|
|
// draw the left & right lines for our control image
|
|
e.Graphics.DrawLine(pendark, bounds.X, bounds.Y + 1, bounds.X, bounds.Y + bounds.Height - 2);
|
|
e.Graphics.DrawLine(pendark, bounds.X + bounds.Width, bounds.Y + 1, bounds.X + bounds.Width, bounds.Y + bounds.Height - 2);
|
|
|
|
if(this.Enabled)
|
|
{
|
|
// draw the arrows for our control image
|
|
// the ArrowPointArray is a point array that defines an arrow shaped polygon
|
|
e.Graphics.FillPolygon(brushdarkdark, ArrowPointArray(bounds.X + 3, bounds.Y + 2));
|
|
e.Graphics.FillPolygon(brushdarkdark, ArrowPointArray(bounds.X + bounds.Width - 9, bounds.Y + 2));
|
|
}
|
|
|
|
// draw the dots for our control image using a loop
|
|
int x = bounds.X + 14;
|
|
int y = bounds.Y + 3;
|
|
|
|
for(int i = 0; i < 30; i++)
|
|
{
|
|
// light dot
|
|
e.Graphics.DrawRectangle(penlightlight, x + 1 + (i * 3), y, 1, 1);
|
|
// dark dot
|
|
e.Graphics.DrawRectangle(pendark, x + (i * 3), y - 1, 1, 1);
|
|
i++;
|
|
// light dot
|
|
e.Graphics.DrawRectangle(penlightlight, x + 1 + (i * 3), y + 2, 1, 1);
|
|
// dark dot
|
|
e.Graphics.DrawRectangle(pendark, x + (i * 3), y + 1, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Helper methods
|
|
|
|
private void ToggleSplitter()
|
|
{
|
|
//mxd. Toggle visibility
|
|
switch(FixedPanel)
|
|
{
|
|
case FixedPanel.Panel1:
|
|
Panel1.Visible = !collapsed;
|
|
if(collapsed)
|
|
{
|
|
storedsplitterdistance = SplitterDistance;
|
|
storedpanel1minsize = Panel1MinSize;
|
|
Panel1MinSize = 0;
|
|
SplitterDistance = 0;
|
|
}
|
|
else
|
|
{
|
|
Panel1MinSize = storedpanel1minsize;
|
|
SplitterDistance = Math.Min(this.Width, storedsplitterdistance);
|
|
}
|
|
break;
|
|
|
|
case FixedPanel.Panel2:
|
|
Panel2.Visible = !collapsed;
|
|
if(collapsed)
|
|
{
|
|
storedpanel2minsize = Panel2MinSize;
|
|
Panel2MinSize = 0;
|
|
}
|
|
else
|
|
{
|
|
Panel2MinSize = storedpanel2minsize;
|
|
}
|
|
|
|
if(Orientation == Orientation.Vertical)
|
|
{
|
|
if(collapsed) storedsplitterdistance = this.Width - SplitterDistance;
|
|
SplitterDistance = (collapsed ? this.Width : Math.Max(0, this.Width - storedsplitterdistance));
|
|
}
|
|
else
|
|
{
|
|
if(collapsed) storedsplitterdistance = this.Height - SplitterDistance;
|
|
SplitterDistance = (collapsed ? this.Height : Math.Max(0, this.Height - storedsplitterdistance));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private int GetSplitPosition()
|
|
{
|
|
switch(FixedPanel)
|
|
{
|
|
case FixedPanel.Panel1:
|
|
return (Panel1.Visible ? SplitterDistance : storedsplitterdistance);
|
|
|
|
case FixedPanel.Panel2:
|
|
if(Panel2.Visible)
|
|
{
|
|
if(Orientation == Orientation.Vertical)
|
|
return Math.Max(0, this.Width - SplitterDistance);
|
|
else
|
|
return Math.Max(0, this.Height - SplitterDistance);
|
|
}
|
|
else
|
|
{
|
|
return storedsplitterdistance;
|
|
}
|
|
}
|
|
|
|
return SplitterDistance;
|
|
}
|
|
|
|
// This creates a point array to draw a arrow-like polygon
|
|
private Point[] ArrowPointArray(int x, int y)
|
|
{
|
|
Point[] points = new Point[3];
|
|
|
|
// Right or left arrows
|
|
if(Orientation == Orientation.Vertical)
|
|
{
|
|
if((FixedPanel == FixedPanel.Panel2 && Panel2.Visible) || (FixedPanel == FixedPanel.Panel1 && !Panel1.Visible)) // Right arrow
|
|
{
|
|
points[0] = new Point(x, y);
|
|
points[1] = new Point(x + 3, y + 3);
|
|
points[2] = new Point(x, y + 6);
|
|
}
|
|
else // Left arrow
|
|
{
|
|
points[0] = new Point(x + 3, y);
|
|
points[1] = new Point(x, y + 3);
|
|
points[2] = new Point(x + 3, y + 6);
|
|
}
|
|
}
|
|
else // Up or down arrows
|
|
{
|
|
if((FixedPanel == FixedPanel.Panel2 && Panel2.Visible) || (FixedPanel == FixedPanel.Panel1 && !Panel1.Visible)) // Down arrow
|
|
{
|
|
points[0] = new Point(x, y);
|
|
points[1] = new Point(x + 6, y);
|
|
points[2] = new Point(x + 3, y + 3);
|
|
}
|
|
else // Up arrow
|
|
{
|
|
points[0] = new Point(x + 3, y);
|
|
points[1] = new Point(x + 6, y + 4);
|
|
points[2] = new Point(x, y + 4);
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
// this method was borrowed from the RichUI Control library by Sajith M
|
|
private static Color CalculateColor(Color front, Color back, int alpha)
|
|
{
|
|
// solid color obtained as a result of alpha-blending
|
|
Color frontColor = Color.FromArgb(255, front);
|
|
Color backColor = Color.FromArgb(255, back);
|
|
|
|
float frontRed = frontColor.R;
|
|
float frontGreen = frontColor.G;
|
|
float frontBlue = frontColor.B;
|
|
float backRed = backColor.R;
|
|
float backGreen = backColor.G;
|
|
float backBlue = backColor.B;
|
|
|
|
float fRed = frontRed * alpha / 255 + backRed * ((float)(255 - alpha) / 255);
|
|
float fGreen = frontGreen * alpha / 255 + backGreen * ((float)(255 - alpha) / 255);
|
|
float fBlue = frontBlue * alpha / 255 + backBlue * ((float)(255 - alpha) / 255);
|
|
|
|
return Color.FromArgb(255, (byte)fRed, (byte)fGreen, (byte)fBlue);
|
|
}
|
|
|
|
// ISupportInitialize methods. Not needed for .Net 4 and higher
|
|
public void BeginInit() { }
|
|
public void EndInit() { }
|
|
|
|
#endregion
|
|
}
|
|
}
|