aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Internal/Splitter.cs
diff options
context:
space:
mode:
authorMichael VanOverbeek <[email protected]>2016-07-25 12:57:52 -0400
committerGitHub <[email protected]>2016-07-25 12:57:52 -0400
commit46c1c31302f111a1f3ec23a70e6f3986a9aa2a27 (patch)
treef00af7ea3f6ad2641fb26fa1d310fd8b7179b39c /source/ShiftUI/Internal/Splitter.cs
parentaf48e774189596b8d7a058c564a7d6d75205ca03 (diff)
parent6fa16209519896de09949a27425dff00ebf2970a (diff)
downloadshiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.tar.gz
shiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.tar.bz2
shiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.zip
Merge pull request #17 from MichaelTheShifter/shiftui_integration
Shiftui integration
Diffstat (limited to 'source/ShiftUI/Internal/Splitter.cs')
-rw-r--r--source/ShiftUI/Internal/Splitter.cs707
1 files changed, 707 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/Splitter.cs b/source/ShiftUI/Internal/Splitter.cs
new file mode 100644
index 0000000..761f6ee
--- /dev/null
+++ b/source/ShiftUI/Internal/Splitter.cs
@@ -0,0 +1,707 @@
+// 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) 2005-2008 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Dennis Bartok ([email protected])
+// Ivan N. Zlatev (contact i-nz.net)
+//
+//
+
+// COMPLETE
+
+#undef Debug
+
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultEvent("SplitterMoved")]
+ [Designer("ShiftUI.Design.SplitterDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [DefaultProperty("Dock")]
+ public class Splitter : Widget
+ {
+ #region Local Variables
+ static private Cursor splitter_ns;
+ static private Cursor splitter_we;
+ // XXX this "new" shouldn't be here. Widget shouldn't define border_style as internal.
+ new private BorderStyle border_style;
+ private int min_extra;
+ private int min_size;
+ private int max_size;
+ private int splitter_size; // Size (width or height) of our splitter control
+ private bool horizontal; // true if we've got a horizontal splitter
+ private Widget affected; // The control that the splitter resizes
+ private int split_requested; // If the user requests a position before we have ever laid out the doc
+ private int splitter_prev_move;
+ private Rectangle splitter_rectangle_moving;
+ private int moving_offset;
+ #endregion // Local Variables
+
+ #region Constructors
+ static Splitter() {
+ splitter_ns = Cursors.HSplit;
+ splitter_we = Cursors.VSplit;
+ }
+
+ public Splitter() {
+
+ min_extra = 25;
+ min_size = 25;
+ split_requested = -1;
+ splitter_size = 3;
+ horizontal = false;
+
+ SetStyle(Widgetstyles.Selectable, false);
+ Anchor = AnchorStyles.None;
+ Dock = DockStyle.Left;
+
+ Layout += new LayoutEventHandler(LayoutSplitter);
+ this.ParentChanged += new EventHandler(ReparentSplitter);
+ Cursor = splitter_we;
+ }
+ #endregion // Constructors
+
+ #region Public Instance Properties
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool AllowDrop {
+ get {
+ return base.AllowDrop;
+ }
+
+ set {
+ base.AllowDrop = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(AnchorStyles.None)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override AnchorStyles Anchor {
+ get {
+ return AnchorStyles.None;
+ }
+
+ set {
+ ; // MS doesn't set it
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ 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; }
+ }
+
+ [DispId(-504)]
+ [DefaultValue (BorderStyle.None)]
+ [MWFDescription("Sets the border style for the splitter")]
+ [MWFCategory("Appearance")]
+ public BorderStyle BorderStyle {
+ get {
+ return border_style;
+ }
+
+ set {
+ border_style = value;
+
+ switch(value) {
+ case BorderStyle.FixedSingle:
+ splitter_size = 4; // We don't get motion events for 1px wide windows on X11. sigh.
+ break;
+
+ case BorderStyle.Fixed3D:
+ value = BorderStyle.None;
+ splitter_size = 3;
+ break;
+
+ case BorderStyle.None:
+ splitter_size = 3;
+ break;
+
+ default:
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for BorderStyle", value));
+ }
+
+ base.InternalBorderStyle = value;
+ }
+ }
+
+ [DefaultValue(DockStyle.Left)]
+ [Localizable(true)]
+ public override DockStyle Dock {
+ get {
+ return base.Dock;
+ }
+
+ set {
+ if (!Enum.IsDefined (typeof (DockStyle), value) || (value == DockStyle.None) || (value == DockStyle.Fill)) {
+ throw new ArgumentException("Splitter must be docked left, top, bottom or right");
+ }
+
+ if ((value == DockStyle.Top) || (value == DockStyle.Bottom)) {
+ horizontal = true;
+ Cursor = splitter_ns;
+ } else {
+ horizontal = false;
+ Cursor = splitter_we;
+ }
+ base.Dock = value;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Font Font {
+ get {
+ return base.Font;
+ }
+
+ set {
+ base.Font = value;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color ForeColor {
+ get {
+ return base.ForeColor;
+ }
+
+ set {
+ base.ForeColor = value;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new ImeMode ImeMode {
+ get {
+ return base.ImeMode;
+ }
+
+ set {
+ base.ImeMode = value;
+ }
+ }
+
+ [DefaultValue(25)]
+ [Localizable(true)]
+ [MWFDescription("Sets minimum size of undocked window")]
+ [MWFCategory("Behaviour")]
+ public int MinExtra {
+ get {
+ return min_extra;
+ }
+
+ set {
+ min_extra = value;
+ }
+ }
+
+ [DefaultValue(25)]
+ [Localizable(true)]
+ [MWFDescription("Sets minimum size of the resized control")]
+ [MWFCategory("Behaviour")]
+ public int MinSize {
+ get {
+ return min_size;
+ }
+
+ set {
+ min_size = value;
+ }
+ }
+
+ internal int MaxSize {
+ get {
+ if (this.Parent == null)
+ return 0;
+
+ if (affected == null)
+ affected = AffectedControl;
+
+ int widths = 0;
+ int heights = 0;
+ int vert_offset = 0;
+ int horiz_offset = 0;
+ foreach (Widget c in this.Parent.Widgets) {
+ if (c != affected) {
+ switch (c.Dock) {
+ case DockStyle.Left:
+ case DockStyle.Right:
+ widths += c.Width;
+
+ if (c.Location.X < this.Location.X)
+ vert_offset += c.Width;
+ break;
+ case DockStyle.Top:
+ case DockStyle.Bottom:
+ heights += c.Height;
+
+ if (c.Location.Y < this.Location.Y)
+ horiz_offset += c.Height;
+ break;
+ }
+ }
+ }
+
+ if (horizontal) {
+ moving_offset = horiz_offset;
+
+ return Parent.ClientSize.Height - heights - MinExtra;
+ } else {
+ moving_offset = vert_offset;
+
+ return Parent.ClientSize.Width - widths - MinExtra;
+ }
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MWFDescription("Current splitter position")]
+ [MWFCategory("Layout")]
+ public int SplitPosition {
+ get {
+ affected = AffectedControl;
+ if (affected == null) {
+ return -1;
+ }
+
+ if (Capture) {
+ return CalculateSplitPosition();
+ }
+
+ if (horizontal) {
+ return affected.Height;
+ } else {
+ return affected.Width;
+ }
+ }
+
+ set {
+ if (value > MaxSize)
+ value = MaxSize;
+ if (value < MinSize)
+ value = MinSize;
+
+ affected = AffectedControl;
+ if (affected == null)
+ split_requested = value;
+ else {
+ if (horizontal)
+ affected.Height = value;
+ else
+ affected.Width = value;
+ OnSplitterMoved (new SplitterEventArgs (Left, Top, value, value));
+ }
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [Bindable(false)]
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get {
+ return base.Text;
+ }
+
+ set {
+ base.Text = value;
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override Cursor DefaultCursor {
+ get { return base.DefaultCursor; }
+ }
+
+ protected override ImeMode DefaultImeMode {
+ get {
+ return ImeMode.Disable;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ return new Size (3, 3);
+ }
+ }
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public override string ToString() {
+ return base.ToString () + String.Format(", MinExtra: {0}, MinSize: {1}", min_extra, min_size);
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override void OnKeyDown(KeyEventArgs e) {
+ base.OnKeyDown (e);
+ if (Capture && (e.KeyCode == Keys.Escape)) {
+ Capture = false;
+ SplitterEndMove (Point.Empty, true);
+ }
+ }
+
+ protected override void OnMouseDown(MouseEventArgs e) {
+ base.OnMouseDown (e);
+
+ // Only allow if we are set up properly
+ if (affected == null)
+ affected = AffectedControl;
+ max_size = MaxSize;
+
+ if (affected == null || e.Button != MouseButtons.Left)
+ return;
+
+ Capture = true;
+ SplitterBeginMove (Parent.PointToClient (PointToScreen (new Point (e.X, e.Y))));
+ }
+
+ protected override void OnMouseMove (MouseEventArgs e)
+ {
+ base.OnMouseMove (e);
+
+ if (!Capture || e.Button != MouseButtons.Left || affected == null)
+ return;
+
+ // We need our mouse coordinates relative to our parent
+ SplitterMove (Parent.PointToClient (PointToScreen (e.Location)));
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e)
+ {
+ base.OnMouseUp (e);
+ if (!Capture || e.Button != MouseButtons.Left || affected == null)
+ return;
+
+ SplitterEndMove (Parent.PointToClient (PointToScreen (e.Location)), false);
+ Capture = false;
+ }
+
+ private void SplitterBeginMove (Point location)
+ {
+ splitter_rectangle_moving = new Rectangle (Bounds.X, Bounds.Y,
+ Width, Height);
+ splitter_prev_move = horizontal ? location.Y : location.X;
+ }
+
+ private void SplitterMove (Point location)
+ {
+ int currentMove = horizontal ? location.Y : location.X;
+ int delta = currentMove - splitter_prev_move;
+ Rectangle prev_location = splitter_rectangle_moving;
+ bool moved = false;
+ int min = this.MinSize + moving_offset;
+ int max = max_size + moving_offset;
+
+ if (horizontal) {
+ 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;
+ }
+ }
+ } else {
+ 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;
+ }
+ }
+ }
+
+ if (moved) {
+ splitter_prev_move = currentMove;
+ OnSplitterMoving (new SplitterEventArgs (location.X, location.Y,
+ splitter_rectangle_moving.X,
+ splitter_rectangle_moving.Y));
+ XplatUI.DrawReversibleRectangle (this.Parent.Handle, prev_location, 1);
+ XplatUI.DrawReversibleRectangle (this.Parent.Handle, splitter_rectangle_moving, 1);
+ }
+ }
+
+ private void SplitterEndMove (Point location, bool cancel)
+ {
+ if (!cancel) {
+ // Resize the affected window
+ if (horizontal)
+ affected.Height = CalculateSplitPosition();
+ else
+ affected.Width = CalculateSplitPosition();
+ }
+
+ this.Parent.Refresh (); // to clean up the drag handle artifacts from all Widgets
+ SplitterEventArgs args = new SplitterEventArgs (location.X, location.Y,
+ splitter_rectangle_moving.X,
+ splitter_rectangle_moving.Y);
+ OnSplitterMoved (args);
+ }
+
+ protected virtual void OnSplitterMoved(SplitterEventArgs sevent) {
+ SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovedEvent]);
+ if (eh != null)
+ eh (this, sevent);
+ }
+
+ protected virtual void OnSplitterMoving(SplitterEventArgs sevent) {
+ SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovingEvent]);
+ if (eh != null)
+ eh (this, sevent);
+ }
+
+ protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
+ // enforce our width / height
+ if (horizontal) {
+ splitter_size = height;
+ if (splitter_size < 1) {
+ splitter_size = 3;
+ }
+ base.SetBoundsCore (x, y, width, splitter_size, specified);
+ } else {
+ splitter_size = width;
+ if (splitter_size < 1) {
+ splitter_size = 3;
+ }
+ base.SetBoundsCore (x, y, splitter_size, height, specified);
+ }
+ }
+ #endregion // Protected Instance Methods
+
+ #region Private Properties and Methods
+ private Widget AffectedControl {
+ get {
+ if (Parent == null)
+ return null;
+
+ // Doc says the first control preceeding us in the zorder
+ for (int i = Parent.Widgets.GetChildIndex(this) + 1; i < Parent.Widgets.Count; i++) {
+ switch (Dock) {
+ case DockStyle.Top:
+ if (Top == Parent.Widgets[i].Bottom)
+ return Parent.Widgets[i];
+ break;
+ case DockStyle.Bottom:
+ if (Bottom == Parent.Widgets[i].Top)
+ return Parent.Widgets[i];
+ break;
+ case DockStyle.Left:
+ if (Left == Parent.Widgets[i].Right)
+ return Parent.Widgets[i];
+ break;
+ case DockStyle.Right:
+ if (Right == Parent.Widgets[i].Left)
+ return Parent.Widgets[i];
+ break;
+ }
+ }
+ return null;
+ }
+ }
+
+ private int CalculateSplitPosition() {
+ if (horizontal) {
+ if (Dock == DockStyle.Top)
+ return splitter_rectangle_moving.Y - affected.Top;
+ else
+ return affected.Bottom - splitter_rectangle_moving.Y - splitter_size;
+ } else {
+ if (Dock == DockStyle.Left)
+ return splitter_rectangle_moving.X - affected.Left;
+ else
+ return affected.Right - splitter_rectangle_moving.X - splitter_size;
+ }
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs e) {
+ e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(this.BackColor), e.ClipRectangle);
+ }
+
+ private void LayoutSplitter(object sender, LayoutEventArgs e) {
+ affected = AffectedControl;
+ if (split_requested != -1) {
+ SplitPosition = split_requested;
+ split_requested = -1;
+ }
+ }
+
+ private void ReparentSplitter(object sender, EventArgs e) {
+ affected = null;
+ }
+
+ #endregion // Private Properties and Methods
+
+ #region Events
+ [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 Enter {
+ add { base.Enter += value; }
+ remove { base.Enter -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler FontChanged {
+ add { base.FontChanged += value; }
+ remove { base.FontChanged -= 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 ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler Leave {
+ add { base.Leave += value; }
+ remove { base.Leave -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ static object SplitterMovedEvent = new object ();
+ static object SplitterMovingEvent = new object ();
+
+ public event SplitterEventHandler SplitterMoved {
+ add { Events.AddHandler (SplitterMovedEvent, value); }
+ remove { Events.RemoveHandler (SplitterMovedEvent, value); }
+ }
+
+ public event SplitterEventHandler SplitterMoving {
+ add { Events.AddHandler (SplitterMovingEvent, value); }
+ remove { Events.RemoveHandler (SplitterMovingEvent, value); }
+ }
+ #endregion // Events
+ }
+}