833 lines
25 KiB
C#
833 lines
25 KiB
C#
|
//
|
||
|
// SplitContainer.cs
|
||
|
//
|
||
|
// 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.
|
||
|
//
|
||
|
// Copyright (c) 2006 Jonathan Pobst
|
||
|
// Copyright (c) 2007 Ivan N. Zlatev
|
||
|
//
|
||
|
// Authors:
|
||
|
// Jonathan Pobst (monkey@jpobst.com)
|
||
|
// Ivan N. Zlatev (contact@i-nz.net)
|
||
|
//
|
||
|
|
||
|
using System;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.ComponentModel;
|
||
|
using System.Drawing;
|
||
|
using System.Drawing.Drawing2D;
|
||
|
|
||
|
namespace System.Windows.Forms
|
||
|
{
|
||
|
[ComVisibleAttribute (true)]
|
||
|
[ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
|
||
|
[DefaultEvent ("SplitterMoved")]
|
||
|
[Docking (DockingBehavior.AutoDock)]
|
||
|
[Designer ("System.Windows.Forms.Design.SplitContainerDesigner, " + Consts.AssemblySystem_Design)]
|
||
|
public class SplitContainer : ContainerControl
|
||
|
, ISupportInitialize
|
||
|
{
|
||
|
#region Local Variables
|
||
|
private FixedPanel fixed_panel;
|
||
|
private Orientation orientation;
|
||
|
|
||
|
private int splitter_increment;
|
||
|
private Rectangle splitter_rectangle;
|
||
|
private Rectangle splitter_rectangle_moving;
|
||
|
private Rectangle splitter_rectangle_before_move;
|
||
|
private bool splitter_fixed;
|
||
|
private bool splitter_dragging;
|
||
|
private int splitter_prev_move;
|
||
|
private Cursor restore_cursor;
|
||
|
private double fixed_none_ratio;
|
||
|
|
||
|
private SplitterPanel panel1;
|
||
|
private bool panel1_collapsed;
|
||
|
private int panel1_min_size;
|
||
|
|
||
|
private SplitterPanel panel2;
|
||
|
private bool panel2_collapsed;
|
||
|
private int panel2_min_size;
|
||
|
#endregion
|
||
|
|
||
|
#region Public Events
|
||
|
static object SplitterMovedEvent = new object ();
|
||
|
static object SplitterMovingEvent = new object ();
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
public new event EventHandler AutoSizeChanged {
|
||
|
add { base.AutoSizeChanged += value; }
|
||
|
remove { base.AutoSizeChanged -= value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (true)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Always)]
|
||
|
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 ControlEventHandler ControlAdded {
|
||
|
add { base.ControlAdded += value; }
|
||
|
remove { base.ControlAdded -= value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
public new event ControlEventHandler ControlRemoved {
|
||
|
add { base.ControlRemoved += value; }
|
||
|
remove { base.ControlRemoved -= value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
public new event EventHandler PaddingChanged {
|
||
|
add { base.PaddingChanged += value; }
|
||
|
remove { base.PaddingChanged -= value; }
|
||
|
}
|
||
|
|
||
|
public event SplitterEventHandler SplitterMoved {
|
||
|
add { Events.AddHandler (SplitterMovedEvent, value); }
|
||
|
remove { Events.RemoveHandler (SplitterMovedEvent, value); }
|
||
|
}
|
||
|
|
||
|
public event SplitterCancelEventHandler SplitterMoving {
|
||
|
add { Events.AddHandler (SplitterMovingEvent, value); }
|
||
|
remove { Events.RemoveHandler (SplitterMovingEvent, value); }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
public new event EventHandler TextChanged {
|
||
|
add { base.TextChanged += value; }
|
||
|
remove { base.TextChanged -= value; }
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region UIA Framework Events
|
||
|
static object UIACanResizeChangedEvent = new object ();
|
||
|
|
||
|
internal event EventHandler UIACanResizeChanged {
|
||
|
add { Events.AddHandler (UIACanResizeChangedEvent, value); }
|
||
|
remove { Events.RemoveHandler (UIACanResizeChangedEvent, value); }
|
||
|
}
|
||
|
|
||
|
internal void OnUIACanResizeChanged (EventArgs e)
|
||
|
{
|
||
|
EventHandler eh = (EventHandler) Events [UIACanResizeChangedEvent];
|
||
|
if (eh != null)
|
||
|
eh (this, e);
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Public Constructors
|
||
|
public SplitContainer ()
|
||
|
{
|
||
|
SetStyle (ControlStyles.SupportsTransparentBackColor, true);
|
||
|
SetStyle (ControlStyles.OptimizedDoubleBuffer, true);
|
||
|
|
||
|
fixed_panel = FixedPanel.None;
|
||
|
orientation = Orientation.Vertical;
|
||
|
|
||
|
splitter_rectangle = new Rectangle (50, 0, 4, this.Height);
|
||
|
splitter_increment = 1;
|
||
|
splitter_prev_move = -1;
|
||
|
restore_cursor = null;
|
||
|
|
||
|
splitter_fixed = false;
|
||
|
panel1_collapsed = false;
|
||
|
panel2_collapsed = false;
|
||
|
panel1_min_size = 25;
|
||
|
panel2_min_size = 25;
|
||
|
|
||
|
panel1 = new SplitterPanel (this);
|
||
|
panel2 = new SplitterPanel (this);
|
||
|
panel1.Size = new Size (50, 50);
|
||
|
UpdateSplitter ();
|
||
|
|
||
|
this.Controls.Add (panel2);
|
||
|
this.Controls.Add (panel1);
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Public Properties
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (false)]
|
||
|
public override bool AutoScroll {
|
||
|
get { return base.AutoScroll; }
|
||
|
set { base.AutoScroll = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
||
|
new public Size AutoScrollMargin {
|
||
|
get { return base.AutoScrollMargin; }
|
||
|
set { base.AutoScrollMargin = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
||
|
new public Size AutoScrollMinSize {
|
||
|
get { return base.AutoScrollMinSize; }
|
||
|
set { base.AutoScrollMinSize = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[DefaultValue ("{X=0,Y=0}")]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
public override Point AutoScrollOffset {
|
||
|
get { return base.AutoScrollOffset; }
|
||
|
set { base.AutoScrollOffset = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
||
|
new public Point AutoScrollPosition {
|
||
|
get { return base.AutoScrollPosition; }
|
||
|
set { base.AutoScrollPosition = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
||
|
public override bool AutoSize {
|
||
|
get { return base.AutoSize; }
|
||
|
set { base.AutoSize = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (true)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Always)]
|
||
|
public override Image BackgroundImage {
|
||
|
get { return base.BackgroundImage; }
|
||
|
set { base.BackgroundImage = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
public override ImageLayout BackgroundImageLayout {
|
||
|
get { return base.BackgroundImageLayout; }
|
||
|
set { base.BackgroundImageLayout = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
public override BindingContext BindingContext {
|
||
|
get { return base.BindingContext; }
|
||
|
set { base.BindingContext = value; }
|
||
|
}
|
||
|
|
||
|
// MSDN says default is Fixed3D, creating a new SplitContainer says otherwise.
|
||
|
[DefaultValue (BorderStyle.None)]
|
||
|
[DispId (-504)]
|
||
|
public BorderStyle BorderStyle {
|
||
|
get { return panel1.BorderStyle; }
|
||
|
set {
|
||
|
if (!Enum.IsDefined (typeof (BorderStyle), value))
|
||
|
throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for BorderStyle", value));
|
||
|
|
||
|
panel1.BorderStyle = value;
|
||
|
panel2.BorderStyle = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
||
|
new public ControlCollection Controls { get { return base.Controls; } }
|
||
|
|
||
|
new public DockStyle Dock {
|
||
|
get { return base.Dock; }
|
||
|
set { base.Dock = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue (FixedPanel.None)]
|
||
|
public FixedPanel FixedPanel {
|
||
|
get { return this.fixed_panel; }
|
||
|
set {
|
||
|
if (!Enum.IsDefined (typeof (FixedPanel), value))
|
||
|
throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for FixedPanel", value));
|
||
|
|
||
|
this.fixed_panel = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (false)]
|
||
|
public bool IsSplitterFixed {
|
||
|
get { return splitter_fixed; }
|
||
|
set { splitter_fixed = value; }
|
||
|
}
|
||
|
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (Orientation.Vertical)]
|
||
|
public Orientation Orientation {
|
||
|
get { return this.orientation; }
|
||
|
set {
|
||
|
if (!Enum.IsDefined (typeof (Orientation), value))
|
||
|
throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for Orientation", value));
|
||
|
|
||
|
if (this.orientation != value) {
|
||
|
if (value == Orientation.Vertical) {
|
||
|
splitter_rectangle.Width = splitter_rectangle.Height;
|
||
|
splitter_rectangle.X = splitter_rectangle.Y;
|
||
|
} else {
|
||
|
splitter_rectangle.Height = splitter_rectangle.Width;
|
||
|
splitter_rectangle.Y = splitter_rectangle.X;
|
||
|
}
|
||
|
|
||
|
this.orientation = value;
|
||
|
this.UpdateSplitter ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
||
|
new public Padding Padding {
|
||
|
get { return base.Padding; }
|
||
|
set { base.Padding = value; }
|
||
|
}
|
||
|
|
||
|
[Localizable (false)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
|
||
|
public SplitterPanel Panel1 { get { return this.panel1; } }
|
||
|
|
||
|
[DefaultValue (false)]
|
||
|
public bool Panel1Collapsed {
|
||
|
get { return this.panel1_collapsed; }
|
||
|
set {
|
||
|
if (panel1_collapsed != value) {
|
||
|
this.panel1_collapsed = value;
|
||
|
panel1.Visible = !value;
|
||
|
|
||
|
// UIA Framework Event: CanResize Changed
|
||
|
OnUIACanResizeChanged (EventArgs.Empty);
|
||
|
|
||
|
PerformLayout ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (25)]
|
||
|
[RefreshProperties (RefreshProperties.All)]
|
||
|
public int Panel1MinSize {
|
||
|
get { return this.panel1_min_size; }
|
||
|
set {
|
||
|
this.panel1_min_size = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Localizable (false)]
|
||
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
|
||
|
public SplitterPanel Panel2 { get { return this.panel2; } }
|
||
|
|
||
|
[DefaultValue (false)]
|
||
|
public bool Panel2Collapsed {
|
||
|
get { return this.panel2_collapsed; }
|
||
|
set {
|
||
|
if (panel2_collapsed != value) {
|
||
|
this.panel2_collapsed = value;
|
||
|
panel2.Visible = !value;
|
||
|
|
||
|
// UIA Framework Event: CanResize Changed
|
||
|
OnUIACanResizeChanged (EventArgs.Empty);
|
||
|
|
||
|
PerformLayout ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (25)]
|
||
|
[RefreshProperties (RefreshProperties.All)]
|
||
|
public int Panel2MinSize {
|
||
|
get { return this.panel2_min_size; }
|
||
|
set { this.panel2_min_size = value; }
|
||
|
}
|
||
|
|
||
|
// MSDN says the default is 40, MS's implementation defaults to 50.
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (50)]
|
||
|
[SettingsBindable (true)]
|
||
|
public int SplitterDistance {
|
||
|
get {
|
||
|
if (orientation == Orientation.Vertical)
|
||
|
return this.splitter_rectangle.X;
|
||
|
else
|
||
|
return this.splitter_rectangle.Y;
|
||
|
}
|
||
|
set {
|
||
|
if (value < 0)
|
||
|
throw new ArgumentOutOfRangeException ();
|
||
|
|
||
|
if (value < panel1_min_size)
|
||
|
value = panel1_min_size;
|
||
|
|
||
|
bool updated = true;
|
||
|
if (orientation == Orientation.Vertical) {
|
||
|
if (this.Width - (this.SplitterWidth + value) < panel2_min_size)
|
||
|
value = this.Width - (this.SplitterWidth + panel2_min_size);
|
||
|
if (splitter_rectangle.X != value) {
|
||
|
splitter_rectangle.X = value;
|
||
|
updated = true;
|
||
|
}
|
||
|
} else {
|
||
|
if (this.Height - (this.SplitterWidth + value) < panel2_min_size)
|
||
|
value = this.Height - (this.SplitterWidth + panel2_min_size);
|
||
|
if (splitter_rectangle.Y != value) {
|
||
|
splitter_rectangle.Y = value;
|
||
|
updated = true;
|
||
|
}
|
||
|
}
|
||
|
if (updated) {
|
||
|
UpdateSplitter ();
|
||
|
OnSplitterMoved (new SplitterEventArgs (Left, Top, splitter_rectangle.X, splitter_rectangle.Y));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (1)]
|
||
|
[MonoTODO ("Stub, never called")]
|
||
|
public int SplitterIncrement {
|
||
|
get { return this.splitter_increment; }
|
||
|
set { this.splitter_increment = value; }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
public Rectangle SplitterRectangle { get { return splitter_rectangle; } }
|
||
|
|
||
|
[Localizable (true)]
|
||
|
[DefaultValue (4)]
|
||
|
public int SplitterWidth {
|
||
|
get {
|
||
|
if (orientation == Orientation.Vertical)
|
||
|
return this.splitter_rectangle.Width;
|
||
|
else
|
||
|
return this.splitter_rectangle.Height;
|
||
|
}
|
||
|
set {
|
||
|
if (value < 1)
|
||
|
throw new ArgumentOutOfRangeException ();
|
||
|
|
||
|
if (orientation == Orientation.Vertical)
|
||
|
this.splitter_rectangle.Width = value;
|
||
|
else
|
||
|
this.splitter_rectangle.Height = value;
|
||
|
UpdateSplitter ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DispId (-516)]
|
||
|
[DefaultValue (true)]
|
||
|
[MonoTODO ("Stub, never called")]
|
||
|
new public bool TabStop {
|
||
|
get { return false; }
|
||
|
set { }
|
||
|
}
|
||
|
|
||
|
[Browsable (false)]
|
||
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
||
|
[Bindable (false)]
|
||
|
public override string Text {
|
||
|
get { return base.Text; }
|
||
|
set { base.Text = value; }
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Protected Properties
|
||
|
protected override Size DefaultSize { get { return new Size (150, 100); } }
|
||
|
#endregion
|
||
|
|
||
|
#region Public Methods
|
||
|
[MonoTODO]
|
||
|
public void BeginInit ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public void EndInit ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public void OnSplitterMoved (SplitterEventArgs e)
|
||
|
{
|
||
|
SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovedEvent]);
|
||
|
if (eh != null)
|
||
|
eh (this, e);
|
||
|
}
|
||
|
|
||
|
public void OnSplitterMoving (SplitterCancelEventArgs e)
|
||
|
{
|
||
|
SplitterCancelEventHandler eh = (SplitterCancelEventHandler)(Events [SplitterMovingEvent]);
|
||
|
if (eh != null)
|
||
|
eh (this, e);
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Protected Methods
|
||
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
||
|
protected override ControlCollection CreateControlsInstance ()
|
||
|
{
|
||
|
return new SplitContainerTypedControlCollection (this);
|
||
|
}
|
||
|
|
||
|
protected override void OnGotFocus (EventArgs e)
|
||
|
{
|
||
|
base.OnGotFocus (e);
|
||
|
}
|
||
|
|
||
|
protected override void OnKeyDown (KeyEventArgs e)
|
||
|
{
|
||
|
base.OnKeyDown (e);
|
||
|
}
|
||
|
|
||
|
protected override void OnKeyUp (KeyEventArgs e)
|
||
|
{
|
||
|
base.OnKeyUp (e);
|
||
|
}
|
||
|
|
||
|
protected override void OnLayout (LayoutEventArgs e)
|
||
|
{
|
||
|
UpdateLayout ();
|
||
|
base.OnLayout (e);
|
||
|
}
|
||
|
|
||
|
protected override void OnLostFocus (EventArgs e)
|
||
|
{
|
||
|
base.OnLostFocus (e);
|
||
|
}
|
||
|
|
||
|
protected override void OnMouseCaptureChanged (EventArgs e)
|
||
|
{
|
||
|
base.OnMouseCaptureChanged (e);
|
||
|
}
|
||
|
|
||
|
protected override void OnMouseDown (MouseEventArgs e)
|
||
|
{
|
||
|
base.OnMouseDown (e);
|
||
|
if (!splitter_fixed && SplitterHitTest (e.Location)) {
|
||
|
splitter_dragging = true;
|
||
|
SplitterBeginMove (e.Location);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnMouseLeave (EventArgs e)
|
||
|
{
|
||
|
base.OnMouseLeave (e);
|
||
|
SplitterRestoreCursor ();
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
||
|
protected override void OnMouseMove (MouseEventArgs e)
|
||
|
{
|
||
|
base.OnMouseMove (e);
|
||
|
|
||
|
if (splitter_dragging)
|
||
|
SplitterMove (e.Location);
|
||
|
|
||
|
if (!splitter_fixed && SplitterHitTest (e.Location))
|
||
|
SplitterSetCursor (orientation);
|
||
|
|
||
|
}
|
||
|
|
||
|
protected override void OnMouseUp (MouseEventArgs e)
|
||
|
{
|
||
|
base.OnMouseUp (e);
|
||
|
if (splitter_dragging) {
|
||
|
SplitterEndMove (e.Location, false);
|
||
|
SplitterRestoreCursor ();
|
||
|
splitter_dragging = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnPaint (PaintEventArgs e)
|
||
|
{
|
||
|
base.OnPaint (e);
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
||
|
protected override void OnRightToLeftChanged (EventArgs e)
|
||
|
{
|
||
|
base.OnRightToLeftChanged (e);
|
||
|
}
|
||
|
|
||
|
protected override bool ProcessDialogKey (Keys keyData)
|
||
|
{
|
||
|
return base.ProcessDialogKey (keyData);
|
||
|
}
|
||
|
|
||
|
protected override bool ProcessTabKey (bool forward)
|
||
|
{
|
||
|
return base.ProcessTabKey (forward);
|
||
|
}
|
||
|
|
||
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
||
|
protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
|
||
|
{
|
||
|
base.ScaleControl (factor, specified);
|
||
|
}
|
||
|
|
||
|
protected override void Select (bool directed, bool forward)
|
||
|
{
|
||
|
base.Select (directed, forward);
|
||
|
}
|
||
|
|
||
|
protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
|
||
|
{
|
||
|
base.SetBoundsCore (x, y, width, height, specified);
|
||
|
}
|
||
|
|
||
|
protected override void WndProc (ref Message msg)
|
||
|
{
|
||
|
base.WndProc (ref msg);
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Private Methods
|
||
|
|
||
|
private bool SplitterHitTest (Point location)
|
||
|
{
|
||
|
if (location.X >= splitter_rectangle.X &&
|
||
|
location.X <= splitter_rectangle.X + splitter_rectangle.Width &&
|
||
|
location.Y >= splitter_rectangle.Y &&
|
||
|
location.Y <= splitter_rectangle.Y + splitter_rectangle.Height) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private void SplitterBeginMove (Point location)
|
||
|
{
|
||
|
splitter_prev_move = orientation == Orientation.Vertical ? location.X : location.Y;
|
||
|
splitter_rectangle_moving = splitter_rectangle;
|
||
|
splitter_rectangle_before_move = splitter_rectangle;
|
||
|
}
|
||
|
|
||
|
private void SplitterMove (Point location)
|
||
|
{
|
||
|
int currentMove = orientation == Orientation.Vertical ? location.X : location.Y;
|
||
|
int delta = currentMove - splitter_prev_move;
|
||
|
Rectangle prev_location = splitter_rectangle_moving;
|
||
|
bool moved = false;
|
||
|
|
||
|
if (orientation == Orientation.Vertical) {
|
||
|
int min = panel1_min_size;
|
||
|
int max = panel2.Location.X + (panel2.Width - this.panel2_min_size) - splitter_rectangle_moving.Width;
|
||
|
|
||
|
if (splitter_rectangle_moving.X + delta > min && splitter_rectangle_moving.X + delta < max) {
|
||
|
splitter_rectangle_moving.X += delta;
|
||
|
moved = true;
|
||
|
} else {
|
||
|
// Ensure that the splitter is set to minimum or maximum position,
|
||
|
// even if the mouse "skips".
|
||
|
//
|
||
|
if (splitter_rectangle_moving.X + delta <= min && splitter_rectangle_moving.X != min) {
|
||
|
splitter_rectangle_moving.X = min;
|
||
|
moved = true;
|
||
|
} else if (splitter_rectangle_moving.X + delta >= max && splitter_rectangle_moving.X != max) {
|
||
|
splitter_rectangle_moving.X = max;
|
||
|
moved = true;
|
||
|
}
|
||
|
}
|
||
|
} else if (orientation == Orientation.Horizontal) {
|
||
|
int min = panel1_min_size;
|
||
|
int max = panel2.Location.Y + (panel2.Height - this.panel2_min_size) - splitter_rectangle_moving.Height;
|
||
|
|
||
|
if (splitter_rectangle_moving.Y + delta > min && splitter_rectangle_moving.Y + delta < max) {
|
||
|
splitter_rectangle_moving.Y += delta;
|
||
|
moved = true;
|
||
|
} else {
|
||
|
// Ensure that the splitter is set to minimum or maximum position,
|
||
|
// even if the mouse "skips".
|
||
|
//
|
||
|
if (splitter_rectangle_moving.Y + delta <= min && splitter_rectangle_moving.Y != min) {
|
||
|
splitter_rectangle_moving.Y = min;
|
||
|
moved = true;
|
||
|
} else if (splitter_rectangle_moving.Y + delta >= max && splitter_rectangle_moving.Y != max) {
|
||
|
splitter_rectangle_moving.Y = max;
|
||
|
moved = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (moved) {
|
||
|
splitter_prev_move = currentMove;
|
||
|
OnSplitterMoving (new SplitterCancelEventArgs (location.X, location.Y,
|
||
|
splitter_rectangle.X, splitter_rectangle.Y));
|
||
|
XplatUI.DrawReversibleRectangle (this.Handle, prev_location, 1);
|
||
|
XplatUI.DrawReversibleRectangle (this.Handle, splitter_rectangle_moving, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void SplitterEndMove (Point location, bool cancel)
|
||
|
{
|
||
|
if (!cancel) {
|
||
|
// Prevent updating the splitter distance if the user changes it in e.g. the
|
||
|
// DoubleClick handler, but no delta move has happened in our drag-handling.
|
||
|
// We don't compare to splitter_rectangle for exactly that reason here
|
||
|
// (if it gets changed externally) and compare to a cached value.
|
||
|
//
|
||
|
if (splitter_rectangle_before_move != splitter_rectangle_moving) {
|
||
|
splitter_rectangle = splitter_rectangle_moving;
|
||
|
UpdateSplitter ();
|
||
|
}
|
||
|
}
|
||
|
SplitterEventArgs args = new SplitterEventArgs (location.X, location.Y,
|
||
|
splitter_rectangle.X, splitter_rectangle.Y);
|
||
|
OnSplitterMoved (args);
|
||
|
}
|
||
|
|
||
|
private void SplitterSetCursor (Orientation orientation)
|
||
|
{
|
||
|
if (restore_cursor == null)
|
||
|
restore_cursor = this.Cursor;
|
||
|
this.Cursor = orientation == Orientation.Vertical ? Cursors.VSplit : Cursors.HSplit;
|
||
|
}
|
||
|
|
||
|
private void SplitterRestoreCursor ()
|
||
|
{
|
||
|
if (restore_cursor != null) {
|
||
|
this.Cursor = restore_cursor;
|
||
|
restore_cursor = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void UpdateSplitter ()
|
||
|
{
|
||
|
this.SuspendLayout ();
|
||
|
panel1.SuspendLayout ();
|
||
|
panel2.SuspendLayout ();
|
||
|
|
||
|
if (panel1_collapsed) {
|
||
|
panel2.Size = this.Size;
|
||
|
panel2.Location = new Point (0, 0);
|
||
|
} else if (panel2_collapsed) {
|
||
|
panel1.Size = this.Size;
|
||
|
panel1.Location = new Point (0, 0);
|
||
|
} else {
|
||
|
panel1.Location = new Point (0, 0);
|
||
|
if (orientation == Orientation.Vertical) {
|
||
|
splitter_rectangle.Y = 0;
|
||
|
panel1.InternalHeight = panel2.InternalHeight = this.Height;
|
||
|
panel1.InternalWidth = Math.Max (this.SplitterDistance, panel1_min_size);
|
||
|
panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
|
||
|
panel2.InternalWidth = Math.Max (this.Width - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
|
||
|
fixed_none_ratio = (double) this.Width / (double)this.SplitterDistance;
|
||
|
} else if (orientation == Orientation.Horizontal) {
|
||
|
splitter_rectangle.X = 0;
|
||
|
panel1.InternalWidth = panel2.InternalWidth = this.Width;
|
||
|
panel1.InternalHeight = Math.Max (this.SplitterDistance, panel1_min_size);
|
||
|
panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
|
||
|
panel2.InternalHeight = Math.Max (this.Height - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
|
||
|
fixed_none_ratio = (double) this.Height / (double)this.SplitterDistance;
|
||
|
}
|
||
|
}
|
||
|
panel1.ResumeLayout ();
|
||
|
panel2.ResumeLayout ();
|
||
|
this.ResumeLayout ();
|
||
|
}
|
||
|
|
||
|
private void UpdateLayout ()
|
||
|
{
|
||
|
panel1.SuspendLayout ();
|
||
|
panel2.SuspendLayout ();
|
||
|
|
||
|
if (panel1_collapsed) {
|
||
|
panel2.Size = this.Size;
|
||
|
panel2.Location = new Point (0, 0);
|
||
|
} else if (panel2_collapsed) {
|
||
|
panel1.Size = this.Size;
|
||
|
panel1.Location = new Point (0, 0);
|
||
|
} else {
|
||
|
panel1.Location = new Point (0, 0);
|
||
|
if (orientation == Orientation.Vertical) {
|
||
|
panel1.Location = new Point (0, 0);
|
||
|
panel1.InternalHeight = panel2.InternalHeight = this.Height;
|
||
|
splitter_rectangle.Height = this.Height;
|
||
|
|
||
|
if (fixed_panel == FixedPanel.None) {
|
||
|
splitter_rectangle.X = Math.Max ((int)Math.Floor (((double)this.Width) / fixed_none_ratio), panel1_min_size); //set distance
|
||
|
panel1.InternalWidth = this.SplitterDistance;
|
||
|
panel2.InternalWidth = this.Width - (this.SplitterWidth + this.SplitterDistance);
|
||
|
panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
|
||
|
} else if (fixed_panel == FixedPanel.Panel1) {
|
||
|
panel1.InternalWidth = this.SplitterDistance;
|
||
|
panel2.InternalWidth = Math.Max (this.Width - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
|
||
|
panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
|
||
|
} else if (fixed_panel == FixedPanel.Panel2) {
|
||
|
splitter_rectangle.X = Math.Max (this.Width - (this.SplitterWidth + panel2.Width), panel1_min_size); //set distance
|
||
|
panel1.InternalWidth = this.SplitterDistance;
|
||
|
panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
|
||
|
}
|
||
|
} else if (orientation == Orientation.Horizontal) {
|
||
|
panel1.Location = new Point (0, 0);
|
||
|
panel1.InternalWidth = panel2.InternalWidth = this.Width;
|
||
|
splitter_rectangle.Width = this.Width;
|
||
|
|
||
|
if (fixed_panel == FixedPanel.None) {
|
||
|
splitter_rectangle.Y = Math.Max ((int) Math.Floor ((double)this.Height / fixed_none_ratio), panel1_min_size); //set distance
|
||
|
panel1.InternalHeight = this.SplitterDistance;
|
||
|
panel2.InternalHeight = this.Height - (this.SplitterWidth + this.SplitterDistance);
|
||
|
panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
|
||
|
} else if (fixed_panel == FixedPanel.Panel1) {
|
||
|
panel1.InternalHeight = this.SplitterDistance;
|
||
|
panel2.InternalHeight = Math.Max (this.Height - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
|
||
|
panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
|
||
|
} else if (fixed_panel == FixedPanel.Panel2) {
|
||
|
splitter_rectangle.Y = Math.Max (this.Height - (this.SplitterWidth + panel2.Height), panel1_min_size); //set distance
|
||
|
panel1.InternalHeight = this.SplitterDistance;
|
||
|
panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
panel1.ResumeLayout ();
|
||
|
panel2.ResumeLayout ();
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Internal Classes
|
||
|
internal class SplitContainerTypedControlCollection : ControlCollection
|
||
|
{
|
||
|
public SplitContainerTypedControlCollection (Control owner) : base (owner)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
#endregion
|
||
|
}
|
||
|
}
|