diff options
| author | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
|---|---|---|
| committer | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
| commit | d40fed5ce2bc806a91245adb18039634eac13ed0 (patch) | |
| tree | f1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Internal/Splitter.cs | |
| parent | f1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff) | |
| download | shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2 shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip | |
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Internal/Splitter.cs')
| -rw-r--r-- | source/ShiftUI/Internal/Splitter.cs | 707 |
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 + } +} |
