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/ScrollableControl.cs | |
| parent | f1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff) | |
| download | shiftos-c-_theultimatehacker-d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz shiftos-c-_theultimatehacker-d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2 shiftos-c-_theultimatehacker-d40fed5ce2bc806a91245adb18039634eac13ed0.zip | |
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/ScrollableControl.cs')
| -rw-r--r-- | source/ShiftUI/ScrollableControl.cs | 1024 |
1 files changed, 1024 insertions, 0 deletions
diff --git a/source/ShiftUI/ScrollableControl.cs b/source/ShiftUI/ScrollableControl.cs new file mode 100644 index 0000000..811ce20 --- /dev/null +++ b/source/ShiftUI/ScrollableControl.cs @@ -0,0 +1,1024 @@ +// 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) 2004 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace ShiftUI { + //[Designer ("ShiftUI.Design.ScrollableWidgetDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")] + [ClassInterface (ClassInterfaceType.AutoDispatch)] + [ComVisible (true)] + public class ScrollableWidget : Widget { + #region Local Variables + private bool force_hscroll_visible; + private bool force_vscroll_visible; + private bool auto_scroll; + private Size auto_scroll_margin; + private Size auto_scroll_min_size; + private Point scroll_position; + private DockPaddingEdges dock_padding; + private SizeGrip sizegrip; + internal ImplicitHScrollBar hscrollbar; + internal ImplicitVScrollBar vscrollbar; + internal Size canvas_size; + private Rectangle display_rectangle; + private Widget old_parent; + private HScrollProperties horizontalScroll; + private VScrollProperties verticalScroll; + private bool autosized_child; + #endregion // Local Variables + + [TypeConverter(typeof(ScrollableWidget.DockPaddingEdgesConverter))] + #region Subclass DockPaddingEdges + public class DockPaddingEdges : ICloneable + { + private Widget owner; + + internal DockPaddingEdges (Widget owner) + { + this.owner = owner; + } + + #region DockPaddingEdges Public Instance Properties + [RefreshProperties (RefreshProperties.All)] + public int All { + get { return owner.Padding.All; } + set { owner.Padding = new Padding (value); } + } + + [RefreshProperties (RefreshProperties.All)] + public int Bottom { + get { return owner.Padding.Bottom; } + set { owner.Padding = new Padding (Left, Top, Right, value); } + } + + [RefreshProperties (RefreshProperties.All)] + public int Left { + get { return owner.Padding.Left; } + set { owner.Padding = new Padding (value, Top, Right, Bottom); } + } + + [RefreshProperties (RefreshProperties.All)] + public int Right { + get { return owner.Padding.Right; } + set { owner.Padding = new Padding (Left, Top, value, Bottom); } + } + + [RefreshProperties (RefreshProperties.All)] + public int Top { + get { return owner.Padding.Top; } + set { owner.Padding = new Padding (Left, value, Right, Bottom); } + } + #endregion // DockPaddingEdges Public Instance Properties + + // Public Instance Methods + public override bool Equals (object other) + { + if (!(other is DockPaddingEdges)) { + return false; + } + + if ((this.All == ((DockPaddingEdges)other).All) && (this.Left == ((DockPaddingEdges)other).Left) && + (this.Right == ((DockPaddingEdges)other).Right) && (this.Top == ((DockPaddingEdges)other).Top) && + (this.Bottom == ((DockPaddingEdges)other).Bottom)) { + return true; + } + + return false; + } + + public override int GetHashCode () + { + return All * Top * Bottom * Right * Left; + } + + public override string ToString () + { + return "All = " + All.ToString () + " Top = " + Top.ToString () + " Left = " + Left.ToString () + " Bottom = " + Bottom.ToString () + " Right = " + Right.ToString (); + } + + internal void Scale (float dx, float dy) + { + Left = (int)(Left * dx); + Right = (int)(Right * dx); + Top = (int)(Top * dy); + Bottom = (int)(Bottom * dy); + } + + object ICloneable.Clone () + { + return new DockPaddingEdges (owner); + } + } + #endregion // Subclass DockPaddingEdges + + #region Subclass DockPaddingEdgesConverter + public class DockPaddingEdgesConverter : System.ComponentModel.TypeConverter { + // Public Constructors + public DockPaddingEdgesConverter() { + } + + // Public Instance Methods + public override PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, Attribute[] attributes) { + return TypeDescriptor.GetProperties(typeof(DockPaddingEdges), attributes); + } + + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { + return true; + } + } + #endregion // Subclass DockPaddingEdgesConverter + + #region Public Constructors + public ScrollableWidget() { + SetStyle(Widgetstyles.ContainerWidget, true); + SetStyle(Widgetstyles.AllPaintingInWmPaint, false); + + auto_scroll = false; + force_hscroll_visible = false; + force_vscroll_visible = false; + auto_scroll_margin = new Size(0, 0); + auto_scroll_min_size = new Size(0, 0); + scroll_position = new Point(0, 0); + SizeChanged +=new EventHandler(Recalculate); + VisibleChanged += new EventHandler (VisibleChangedHandler); + LocationChanged += new EventHandler (LocationChangedHandler); + ParentChanged += new EventHandler (ParentChangedHandler); + HandleCreated += new EventHandler (AddScrollbars); + + CreateScrollbars (); + + horizontalScroll = new HScrollProperties (this); + verticalScroll = new VScrollProperties (this); + } + + void VisibleChangedHandler (object sender, EventArgs e) + { + Recalculate (false); + } + + void LocationChangedHandler (object sender, EventArgs e) + { + UpdateSizeGripVisible (); + } + + void ParentChangedHandler (object sender, EventArgs e) + { + + if (old_parent == Parent) + return; + + if (old_parent != null) { + old_parent.SizeChanged -= new EventHandler (Parent_SizeChanged); + old_parent.PaddingChanged -= new EventHandler (Parent_PaddingChanged); + } + + if (Parent != null) { + Parent.SizeChanged += new EventHandler (Parent_SizeChanged); + Parent.PaddingChanged += new EventHandler (Parent_PaddingChanged); + } + + old_parent = Parent; + } + + void Parent_PaddingChanged (object sender, EventArgs e) + { + UpdateSizeGripVisible (); + } + + void Parent_SizeChanged (object sender, EventArgs e) + { + UpdateSizeGripVisible (); + } + #endregion // Public Constructors + + #region Protected Static Fields + protected const int ScrollStateAutoScrolling = 1; + protected const int ScrollStateFullDrag = 16; + protected const int ScrollStateHScrollVisible = 2; + protected const int ScrollStateUserHasScrolled = 8; + protected const int ScrollStateVScrollVisible = 4; + #endregion // Protected Static Fields + + #region Public Instance Properties + [DefaultValue(false)] + [Localizable(true)] + [MWFCategory("Layout")] + public virtual bool AutoScroll { + get { + return auto_scroll; + } + + set { + if (auto_scroll != value) { + auto_scroll = value; + PerformLayout (this, "AutoScroll"); + } + } + } + + [Localizable(true)] + [MWFCategory("Layout")] + public Size AutoScrollMargin { + get { + return auto_scroll_margin; + } + + set { + if (value.Width < 0) { + throw new ArgumentException("Width is assigned less than 0", "value.Width"); + } + + if (value.Height < 0) { + throw new ArgumentException("Height is assigned less than 0", "value.Height"); + } + + auto_scroll_margin = value; + } + } + + internal bool ShouldSerializeAutoScrollMargin () + { + return this.AutoScrollMargin != new Size (0, 0); + } + + [Localizable(true)] + [MWFCategory("Layout")] + public Size AutoScrollMinSize { + get { + return auto_scroll_min_size; + } + + set { + if (value != auto_scroll_min_size) { + auto_scroll_min_size = value; + AutoScroll = true; + PerformLayout (this, "AutoScrollMinSize"); + } + } + } + + internal bool ShouldSerializeAutoScrollMinSize () + { + return this.AutoScrollMinSize != new Size (0, 0); + } + + [Browsable(false)] + //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Point AutoScrollPosition { + get { + return DisplayRectangle.Location; + } + + set { + if (value != AutoScrollPosition) { + int shift_x; + int shift_y; + + shift_x = 0; + shift_y = 0; + if (hscrollbar.VisibleInternal) { + int max = hscrollbar.Maximum - hscrollbar.LargeChange + 1; + value.X = value.X < hscrollbar.Minimum ? hscrollbar.Minimum : value.X; + value.X = value.X > max ? max : value.X; + shift_x = value.X - scroll_position.X; + } + + if (vscrollbar.VisibleInternal) { + int max = vscrollbar.Maximum - vscrollbar.LargeChange + 1; + value.Y = value.Y < vscrollbar.Minimum ? vscrollbar.Minimum : value.Y; + value.Y = value.Y > max ? max : value.Y; + shift_y = value.Y - scroll_position.Y; + } + + ScrollWindow(shift_x, shift_y); + + if (hscrollbar.VisibleInternal) { + if (scroll_position.X >= hscrollbar.Minimum && scroll_position.X <= hscrollbar.Maximum) + hscrollbar.Value = scroll_position.X; + } + + if (vscrollbar.VisibleInternal) { + if (scroll_position.Y >= vscrollbar.Minimum && scroll_position.Y <= vscrollbar.Maximum) + vscrollbar.Value = scroll_position.Y; + } + + } + } + } + + public override Rectangle DisplayRectangle { + get { + if (auto_scroll) { + int width; + int height; + + if (canvas_size.Width <= base.DisplayRectangle.Width) { + width = base.DisplayRectangle.Width; + if (vscrollbar.VisibleInternal) { + width -= vscrollbar.Width; + } + } else { + width = canvas_size.Width; + } + + if (canvas_size.Height <= base.DisplayRectangle.Height) { + height = base.DisplayRectangle.Height; + if (hscrollbar.VisibleInternal) { + height -= hscrollbar.Height; + } + } else { + height = canvas_size.Height; + } + + display_rectangle.X = -scroll_position.X; + display_rectangle.Y = -scroll_position.Y; + display_rectangle.Width = Math.Max(auto_scroll_min_size.Width, width); + display_rectangle.Height = Math.Max(auto_scroll_min_size.Height, height); + } + else { + display_rectangle = base.DisplayRectangle; + } + + // DockPadding is the same as Padding (according to documentation) but is + // calculated lazily, so we use Padding here instead. + if (Padding != Padding.Empty) { + display_rectangle.X += Padding.Left; + display_rectangle.Y += Padding.Top; + display_rectangle.Width -= Padding.Horizontal; + display_rectangle.Height -= Padding.Vertical; + } + + return display_rectangle; + } + } + + [MWFCategory("Layout")] + [Browsable (false)] + //[EditorBrowsable (EditorBrowsableState.Never)] + //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public DockPaddingEdges DockPadding { + get { + if (dock_padding == null) + CreateDockPadding (); + + return dock_padding; + } + } + + [Browsable (false)] + //[EditorBrowsable (EditorBrowsableState.Always)] + public HScrollProperties HorizontalScroll { + get { return horizontalScroll; } + } + + [Browsable (false)] + //[EditorBrowsable (EditorBrowsableState.Always)] + public VScrollProperties VerticalScroll { + get { return verticalScroll; } + } + #endregion // Public Instance Properties + + #region Protected Instance Methods + protected override CreateParams CreateParams { + get { + return base.CreateParams; + } + } + + protected bool HScroll { + get { + return hscrollbar.VisibleInternal; + } + + set { + if (!AutoScroll && hscrollbar.VisibleInternal != value) { + force_hscroll_visible = value; + Recalculate (false); + } + } + } + + protected bool VScroll { + get { + return vscrollbar.VisibleInternal; + } + + set { + if (!AutoScroll && vscrollbar.VisibleInternal != value) { + force_vscroll_visible = value; + Recalculate (false); + } + } + } + #endregion // Protected Instance Methods + + #region Public Instance Methods + public void ScrollWidgetIntoView(Widget activeWidget) { + int corner_x; + int corner_y; + + Rectangle within = new Rectangle (); + within.Size = ClientSize; + + if (!AutoScroll || (!hscrollbar.VisibleInternal && !vscrollbar.VisibleInternal)) { + return; + } + + if (!Contains(activeWidget)) { + return; + } + + if (vscrollbar.Visible) { + within.Width -= vscrollbar.Width; + } + if (hscrollbar.Visible) { + within.Height -= hscrollbar.Height; + } + + // Don't scroll if already visible + if (within.Contains (activeWidget.Location) && within.Contains (activeWidget.Right, activeWidget.Bottom)) { + return; + } + + // If the Widget is above the top or the left, move it down and right until it aligns + // with the top/left. + // If the Widget is below the bottom or to the right, move it up/left until it aligns + // with the bottom/right, but do never move it further than the top/left side. + int x_diff = 0, y_diff = 0; + if (activeWidget.Top <= 0 || activeWidget.Height >= within.Height) { + y_diff = -activeWidget.Top; + } else if (activeWidget.Bottom > within.Height) { + y_diff = within.Height - activeWidget.Bottom; + } + if (activeWidget.Left <= 0 || activeWidget.Width >= within.Width) { + x_diff = -activeWidget.Left; + } else if (activeWidget.Right > within.Width) { + x_diff = within.Width - activeWidget.Right; + } + corner_x = hscrollbar.Value - x_diff; + corner_y = vscrollbar.Value - y_diff; + + if (hscrollbar.VisibleInternal) { + if (corner_x > hscrollbar.Maximum) { + corner_x = hscrollbar.Maximum; + } else if (corner_x < hscrollbar.Minimum) { + corner_x = hscrollbar.Minimum; + } + if (corner_x != hscrollbar.Value) { + hscrollbar.Value = corner_x; + } + } + + if (vscrollbar.VisibleInternal) { + if (corner_y > vscrollbar.Maximum) { + corner_y = vscrollbar.Maximum; + } else if (corner_y < vscrollbar.Minimum) { + corner_y = vscrollbar.Minimum; + } + if (corner_y != vscrollbar.Value) { + vscrollbar.Value = corner_y; + } + } + } + + public void SetAutoScrollMargin(int x, int y) { + if (x < 0) { + x = 0; + } + + if (y < 0) { + y = 0; + } + + auto_scroll_margin = new Size(x, y); + Recalculate (false); + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + //[EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void AdjustFormScrollbars(bool displayScrollbars) { + Recalculate (false); + } + + //[EditorBrowsable(EditorBrowsableState.Advanced)] + protected bool GetScrollState(int bit) { + // Internal MS + return false; + } + + //[EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnLayout(LayoutEventArgs levent) { + CalculateCanvasSize (true); + + AdjustFormScrollbars(AutoScroll); // Dunno what the logic is. Passing AutoScroll seems to match MS behaviour + base.OnLayout(levent); + + // The first time through, we just set the canvas to clientsize + // so we could re-layout everything according to the flow. + // This time we want to actually calculate the canvas. + // If a child is autosized, we need to rethink scrollbars as well. (Xamarin bug 18874) + if (this is FlowLayoutPanel || autosized_child) { + CalculateCanvasSize (false); + AdjustFormScrollbars (AutoScroll); + } + } + + //[EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnMouseWheel(MouseEventArgs e) { + if (vscrollbar.VisibleInternal) { + if (e.Delta > 0) { + if (vscrollbar.Minimum < (vscrollbar.Value - vscrollbar.LargeChange)) { + vscrollbar.Value -= vscrollbar.LargeChange; + } else { + vscrollbar.Value = vscrollbar.Minimum; + } + } else { + int maximum_scrollbar_value = vscrollbar.Maximum - vscrollbar.LargeChange + 1; + if (maximum_scrollbar_value > (vscrollbar.Value + vscrollbar.LargeChange)) { + vscrollbar.Value += vscrollbar.LargeChange; + } else { + vscrollbar.Value = maximum_scrollbar_value; + } + } + } + base.OnMouseWheel(e); + } + + //[EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void OnVisibleChanged(EventArgs e) { + if (Visible) { + UpdateChildrenZOrder (); + PerformLayout(this, "Visible"); + } + base.OnVisibleChanged(e); + } + + //[EditorBrowsable (EditorBrowsableState.Never)] + protected override void ScaleCore(float dx, float dy) { + if (dock_padding != null) + dock_padding.Scale(dx, dy); + + base.ScaleCore(dx, dy); + } + + protected override void ScaleWidget (SizeF factor, BoundsSpecified specified) + { + base.ScaleWidget (factor, specified); + } + + protected virtual Point ScrollToWidget (Widget activeWidget) + { + int corner_x; + int corner_y; + + Rectangle within = new Rectangle (); + within.Size = ClientSize; + + if (vscrollbar.Visible) + within.Width -= vscrollbar.Width; + + if (hscrollbar.Visible) + within.Height -= hscrollbar.Height; + + // If the Widget is above the top or the left, move it down and right until it aligns + // with the top/left. + // If the Widget is below the bottom or to the right, move it up/left until it aligns + // with the bottom/right, but do never move it further than the top/left side. + int x_diff = 0, y_diff = 0; + + if (activeWidget.Top <= 0 || activeWidget.Height >= within.Height) + y_diff = -activeWidget.Top; + else if (activeWidget.Bottom > within.Height) + y_diff = within.Height - activeWidget.Bottom; + + if (activeWidget.Left <= 0 || activeWidget.Width >= within.Width) + x_diff = -activeWidget.Left; + else if (activeWidget.Right > within.Width) + x_diff = within.Width - activeWidget.Right; + + corner_x = AutoScrollPosition.X + x_diff; + corner_y = AutoScrollPosition.Y + y_diff; + + return new Point (corner_x, corner_y); + } + + protected void SetDisplayRectLocation(int x, int y) { + // This method is weird. MS documents that the scrollbars are not + // updated. We need to move stuff, but leave the scrollbars as is + + if (x > 0) { + x = 0; + } + + if (y > 0) { + y = 0; + } + + ScrollWindow(scroll_position.X - x , scroll_position.Y - y); + } + + protected void SetScrollState(int bit, bool value) { + //throw new NotImplementedException(); + } + + //[EditorBrowsable(EditorBrowsableState.Advanced)] + protected override void WndProc(ref Message m) { + base.WndProc(ref m); + } + #endregion // Protected Instance Methods + + #region Internal & Private Methods + internal override IntPtr AfterTopMostWidget () + { + // order of scrollbars: + // top = vertical + // sizegrid + // bottom = horizontal + if (hscrollbar != null && hscrollbar.Visible) + return hscrollbar.Handle; + // no need to check for sizegrip since it will only + // be visible if hbar is visible. + if (vscrollbar != null && vscrollbar.Visible) + return hscrollbar.Handle; + + return base.AfterTopMostWidget (); + } + + internal virtual void CalculateCanvasSize (bool canOverride) { + Widget child; + int num_of_children; + int width; + int height; + int extra_width; + int extra_height; + + num_of_children = Widgets.Count; + width = 0; + height = 0; + extra_width = hscrollbar.Value; + extra_height = vscrollbar.Value; + if (dock_padding != null) { + extra_width += dock_padding.Right; + extra_height += dock_padding.Bottom; + } + + autosized_child = false; + for (int i = 0; i < num_of_children; i++) { + child = Widgets[i]; + if (child.AutoSize) + autosized_child = true; + if (child.Dock == DockStyle.Right) { + extra_width += child.Width; + } else if (child.Dock == DockStyle.Bottom) { + extra_height += child.Height; + } + } + + if (!auto_scroll_min_size.IsEmpty) { + width = auto_scroll_min_size.Width; + height = auto_scroll_min_size.Height; + } + + for (int i = 0; i < num_of_children; i++) { + child = Widgets[i]; + + switch(child.Dock) { + case DockStyle.Left: { + if ((child.Right + extra_width) > width) { + width = child.Right + extra_width; + } + continue; + } + + case DockStyle.Top: { + if ((child.Bottom + extra_height) > height) { + height = child.Bottom + extra_height; + } + continue; + } + + case DockStyle.Fill: + case DockStyle.Right: + case DockStyle.Bottom: { + continue; + } + + default: { + AnchorStyles anchor; + + anchor = child.Anchor; + + if (((anchor & AnchorStyles.Left) != 0) && ((anchor & AnchorStyles.Right) == 0)) { + if ((child.Right + extra_width) > width) { + width = child.Right + extra_width; + } + } + + if (((anchor & AnchorStyles.Top) != 0) || ((anchor & AnchorStyles.Bottom) == 0)) { + if ((child.Bottom + extra_height) > height) { + height = child.Bottom + extra_height; + } + } + continue; + } + } + } + + canvas_size.Width = width; + canvas_size.Height = height; + } + + // Normally DockPadding is created lazyly, as observed in the test cases, but some children + // may need to have it always. + internal void CreateDockPadding () + { + if (dock_padding == null) + dock_padding = new DockPaddingEdges (this); + } + + private void Recalculate (object sender, EventArgs e) { + Recalculate (true); + } + + private void Recalculate (bool doLayout) { + if (!IsHandleCreated) { + return; + } + + Size canvas = canvas_size; + Size client = ClientSize; + + canvas.Width += auto_scroll_margin.Width; + canvas.Height += auto_scroll_margin.Height; + + int right_edge = client.Width; + int bottom_edge = client.Height; + int prev_right_edge; + int prev_bottom_edge; + + bool hscroll_visible; + bool vscroll_visible; + + do { + prev_right_edge = right_edge; + prev_bottom_edge = bottom_edge; + + if ((force_hscroll_visible || (canvas.Width > right_edge && auto_scroll)) && client.Width > 0) { + hscroll_visible = true; + bottom_edge = client.Height - SystemInformation.HorizontalScrollBarHeight; + } else { + hscroll_visible = false; + bottom_edge = client.Height; + } + + if ((force_vscroll_visible || (canvas.Height > bottom_edge && auto_scroll)) && client.Height > 0) { + vscroll_visible = true; + right_edge = client.Width - SystemInformation.VerticalScrollBarWidth; + } else { + vscroll_visible = false; + right_edge = client.Width; + } + + } while (right_edge != prev_right_edge || bottom_edge != prev_bottom_edge); + + if (right_edge < 0) right_edge = 0; + if (bottom_edge < 0) bottom_edge = 0; + + Rectangle hscroll_bounds; + Rectangle vscroll_bounds; + + hscroll_bounds = new Rectangle (0, client.Height - SystemInformation.HorizontalScrollBarHeight, + ClientRectangle.Width, SystemInformation.HorizontalScrollBarHeight); + vscroll_bounds = new Rectangle (client.Width - SystemInformation.VerticalScrollBarWidth, 0, + SystemInformation.VerticalScrollBarWidth, ClientRectangle.Height); + + /* the ScrollWindow calls here are needed + * because (this explanation sucks): + * + * when we transition from having a scrollbar to + * not having one, we won't receive a scrollbar + * moved (value changed) event, so we need to + * manually scroll the canvas. + * + * if you can fix this without requiring the + * ScrollWindow calls, pdb and toshok will each + * pay you $5. + */ + + if (!vscrollbar.Visible) { + vscrollbar.Value = 0; + } + if (!hscrollbar.Visible) { + hscrollbar.Value = 0; + } + + /* Manually setting the size of the thumb should be done before + * the other assignments */ + if (hscroll_visible) { + hscrollbar.manual_thumb_size = right_edge; + hscrollbar.LargeChange = right_edge; + hscrollbar.SmallChange = 5; + hscrollbar.Maximum = canvas.Width - 1; + } else { + if (hscrollbar != null && hscrollbar.VisibleInternal) { + ScrollWindow (- scroll_position.X, 0); + } + scroll_position.X = 0; + } + + if (vscroll_visible) { + vscrollbar.manual_thumb_size = bottom_edge; + vscrollbar.LargeChange = bottom_edge; + vscrollbar.SmallChange = 5; + vscrollbar.Maximum = canvas.Height - 1; + } else { + if (vscrollbar != null && vscrollbar.VisibleInternal) { + ScrollWindow (0, - scroll_position.Y); + } + scroll_position.Y = 0; + } + + if (hscroll_visible && vscroll_visible) { + hscroll_bounds.Width -= SystemInformation.VerticalScrollBarWidth; + vscroll_bounds.Height -= SystemInformation.HorizontalScrollBarHeight; + + sizegrip.Bounds = new Rectangle (hscroll_bounds.Right, + vscroll_bounds.Bottom, + SystemInformation.VerticalScrollBarWidth, + SystemInformation.HorizontalScrollBarHeight); + } + + SuspendLayout (); + + hscrollbar.SetBoundsInternal (hscroll_bounds.X, hscroll_bounds.Y, hscroll_bounds.Width, hscroll_bounds.Height, BoundsSpecified.None); + hscrollbar.Visible = hscroll_visible; + if (hscrollbar.Visible) + XplatUI.SetZOrder (hscrollbar.Handle, IntPtr.Zero, true, false); + + vscrollbar.SetBoundsInternal (vscroll_bounds.X, vscroll_bounds.Y, vscroll_bounds.Width, vscroll_bounds.Height, BoundsSpecified.None); + vscrollbar.Visible = vscroll_visible; + if (vscrollbar.Visible) + XplatUI.SetZOrder (vscrollbar.Handle, IntPtr.Zero, true, false); + + UpdateSizeGripVisible (); + + ResumeLayout (doLayout); + + // We should now scroll the active Widget into view, + // the funny part is that ScrollableWidget does not have + // the concept of active Widget. + ContainerWidget container = this as ContainerWidget; + if (container != null && container.ActiveWidget != null) { + ScrollWidgetIntoView (container.ActiveWidget); + } + } + + internal void UpdateSizeGripVisible () + { + if (!IsHandleCreated) { + return; + } + + sizegrip.CapturedWidget = Parent; + // This is really wierd, the size grip is only showing up + // if the bottom right corner of the scrollable Widget is within + // two pixels from the bottom right corner of its parent. + bool show_sizegrip = hscrollbar.VisibleInternal && vscrollbar.VisibleInternal; + bool enable_sizegrip = false; + if (show_sizegrip && Parent != null) { + Point diff = new Point (Parent.ClientRectangle.Bottom - Bottom, Parent.ClientRectangle.Right - Right); + enable_sizegrip = diff.X <= 2 && diff.X >= 0 && diff.Y <= 2 && diff.Y >= 0; + } + sizegrip.Visible = show_sizegrip; + sizegrip.Enabled = enable_sizegrip || sizegrip.Capture; + if (sizegrip.Visible) + XplatUI.SetZOrder (sizegrip.Handle, vscrollbar.Handle, false, false); + } + + private void HandleScrollBar(object sender, EventArgs e) { + if (sender == vscrollbar) { + if (!vscrollbar.Visible) + return; + ScrollWindow(0, vscrollbar.Value- scroll_position.Y); + } else { + if (!hscrollbar.Visible) + return; + ScrollWindow(hscrollbar.Value - scroll_position.X, 0); + } + } + + private void HandleScrollEvent (object sender, ScrollEventArgs args) + { + OnScroll (args); + } + + private void AddScrollbars (object o, EventArgs e) + { + Widgets.AddRangeImplicit (new Widget[] {hscrollbar, vscrollbar, sizegrip}); + HandleCreated -= new EventHandler (AddScrollbars); + } + + private void CreateScrollbars () + { + hscrollbar = new ImplicitHScrollBar (); + hscrollbar.Visible = false; + hscrollbar.ValueChanged += new EventHandler (HandleScrollBar); + hscrollbar.Height = SystemInformation.HorizontalScrollBarHeight; + hscrollbar.use_manual_thumb_size = true; + hscrollbar.Scroll += new ScrollEventHandler (HandleScrollEvent); + + vscrollbar = new ImplicitVScrollBar (); + vscrollbar.Visible = false; + vscrollbar.ValueChanged += new EventHandler (HandleScrollBar); + vscrollbar.Width = SystemInformation.VerticalScrollBarWidth; + vscrollbar.use_manual_thumb_size = true; + vscrollbar.Scroll += new ScrollEventHandler (HandleScrollEvent); + + sizegrip = new SizeGrip (this); + sizegrip.Visible = false; + } + + private void ScrollWindow(int XOffset, int YOffset) { + int num_of_children; + + if (XOffset == 0 && YOffset == 0) { + return; + } + + SuspendLayout(); + + num_of_children = Widgets.Count; + + for (int i = 0; i < num_of_children; i++) { + Widgets[i].Location = new Point (Widgets[i].Left - XOffset, Widgets[i].Top - YOffset); + //Widgets[i].Left -= XOffset; + //Widgets[i].Top -= YOffset; + // Is this faster? Widgets[i].Location -= new Size(XOffset, YOffset); + } + + scroll_position.X += XOffset; + scroll_position.Y += YOffset; + + XplatUI.ScrollWindow (Handle, ClientRectangle, -XOffset, -YOffset, false); + ResumeLayout(false); + } + #endregion // Internal & Private Methods + + static object OnScrollEvent = new object (); + + protected virtual void OnScroll (ScrollEventArgs se) + { + ScrollEventHandler eh = (ScrollEventHandler) (Events [OnScrollEvent]); + if (eh != null) + eh (this, se); + } + + protected override void OnPaddingChanged (EventArgs e) + { + base.OnPaddingChanged (e); + } + + protected override void OnPaintBackground (PaintEventArgs e) + { + base.OnPaintBackground (e); + } + + //[EditorBrowsable (EditorBrowsableState.Advanced)] + protected override void OnRightToLeftChanged (EventArgs e) + { + base.OnRightToLeftChanged (e); + } + + public event ScrollEventHandler Scroll { + add { Events.AddHandler (OnScrollEvent, value); } + remove { Events.RemoveHandler (OnScrollEvent, value); } + } + } +} |
