2 // System.Windows.Forms.ScrollBar.cs
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Copyright (C) 2004-2005, Novell, Inc.
26 // Jordi Mas i Hernandez jordi@ximian.com
33 using System.Drawing.Imaging;
34 using System.Drawing.Drawing2D;
35 using System.ComponentModel;
36 using System.Runtime.InteropServices;
38 namespace System.Windows.Forms
42 [ClassInterface (ClassInterfaceType.AutoDispatch)]
44 [DefaultEvent ("Scroll")]
45 [DefaultProperty ("Value")]
46 public abstract class ScrollBar : Control
48 #region Local Variables
52 private int large_change;
53 private int small_change;
54 internal int scrollbutton_height;
55 internal int scrollbutton_width;
56 private Rectangle first_arrow_area = new Rectangle (); // up or left
57 private Rectangle second_arrow_area = new Rectangle (); // down or right
58 private Rectangle thumb_pos = new Rectangle ();
59 private Rectangle thumb_area = new Rectangle ();
60 internal ButtonState firstbutton_state = ButtonState.Normal;
61 internal ButtonState secondbutton_state = ButtonState.Normal;
62 private bool firstbutton_pressed = false;
63 private bool secondbutton_pressed = false;
64 private bool thumb_pressed = false;
65 private float pixel_per_pos = 0;
66 private Timer timer = new Timer ();
67 private TimerType timer_type;
68 private int thumb_size = 40;
69 private const int thumb_min_size = 8;
70 private const int thumb_notshown_size = 40;
71 internal bool use_manual_thumb_size;
72 internal int manual_thumb_size;
74 internal bool implicit_control;
75 private int lastclick_pos; // Position of the last button-down event
76 private int thumbclick_offset; // Position of the last button-down event relative to the thumb edge
77 private Rectangle dirty;
79 internal ThumbMoving thumb_moving = ThumbMoving.None;
80 bool first_button_entered;
81 bool second_button_entered;
83 #endregion // Local Variables
85 private enum TimerType
93 internal enum ThumbMoving
103 [EditorBrowsable (EditorBrowsableState.Never)]
104 public new event EventHandler AutoSizeChanged {
105 add { base.AutoSizeChanged += value; }
106 remove { base.AutoSizeChanged -= value; }
111 [EditorBrowsable (EditorBrowsableState.Never)]
112 public new event EventHandler BackColorChanged {
113 add { base.BackColorChanged += value; }
114 remove { base.BackColorChanged -= value; }
118 [EditorBrowsable (EditorBrowsableState.Never)]
119 public new event EventHandler BackgroundImageChanged {
120 add { base.BackgroundImageChanged += value; }
121 remove { base.BackgroundImageChanged -= value; }
126 [EditorBrowsable (EditorBrowsableState.Never)]
127 public new event EventHandler BackgroundImageLayoutChanged {
128 add { base.BackgroundImageLayoutChanged += value; }
129 remove { base.BackgroundImageLayoutChanged -= value; }
134 [EditorBrowsable (EditorBrowsableState.Never)]
135 public new event EventHandler Click {
136 add { base.Click += value; }
137 remove { base.Click -= value; }
141 [EditorBrowsable (EditorBrowsableState.Never)]
142 public new event EventHandler DoubleClick {
143 add { base.DoubleClick += value; }
144 remove { base.DoubleClick -= value; }
148 [EditorBrowsable (EditorBrowsableState.Never)]
149 public new event EventHandler FontChanged {
150 add { base.FontChanged += value; }
151 remove { base.FontChanged -= value; }
155 [EditorBrowsable (EditorBrowsableState.Never)]
156 public new event EventHandler ForeColorChanged {
157 add { base.ForeColorChanged += value; }
158 remove { base.ForeColorChanged -= value; }
162 [EditorBrowsable (EditorBrowsableState.Never)]
163 public new event EventHandler ImeModeChanged {
164 add { base.ImeModeChanged += value; }
165 remove { base.ImeModeChanged -= value; }
170 [EditorBrowsable (EditorBrowsableState.Never)]
171 public new event MouseEventHandler MouseClick {
172 add { base.MouseClick += value; }
173 remove { base.MouseClick -= value; }
177 [EditorBrowsable (EditorBrowsableState.Never)]
178 public new event MouseEventHandler MouseDoubleClick {
179 add { base.MouseDoubleClick += value; }
180 remove { base.MouseDoubleClick -= value; }
185 [EditorBrowsable (EditorBrowsableState.Never)]
186 public new event MouseEventHandler MouseDown {
187 add { base.MouseDown += value; }
188 remove { base.MouseDown -= value; }
192 [EditorBrowsable (EditorBrowsableState.Never)]
193 public new event MouseEventHandler MouseMove {
194 add { base.MouseMove += value; }
195 remove { base.MouseMove -= value; }
199 [EditorBrowsable (EditorBrowsableState.Never)]
200 public new event MouseEventHandler MouseUp {
201 add { base.MouseUp += value; }
202 remove { base.MouseUp -= value; }
206 [EditorBrowsable (EditorBrowsableState.Never)]
207 public new event PaintEventHandler Paint {
208 add { base.Paint += value; }
209 remove { base.Paint -= value; }
212 static object ScrollEvent = new object ();
213 static object ValueChangedEvent = new object ();
215 public event ScrollEventHandler Scroll {
216 add { Events.AddHandler (ScrollEvent, value); }
217 remove { Events.RemoveHandler (ScrollEvent, value); }
221 [EditorBrowsable (EditorBrowsableState.Never)]
222 public new event EventHandler TextChanged {
223 add { base.TextChanged += value; }
224 remove { base.TextChanged -= value; }
227 public event EventHandler ValueChanged {
228 add { Events.AddHandler (ValueChangedEvent, value); }
229 remove { Events.RemoveHandler (ValueChangedEvent, value); }
241 timer.Tick += new EventHandler (OnTimer);
242 MouseEnter += new EventHandler (OnMouseEnter);
243 MouseLeave += new EventHandler (OnMouseLeave);
244 base.KeyDown += new KeyEventHandler (OnKeyDownSB);
245 base.MouseDown += new MouseEventHandler (OnMouseDownSB);
246 base.MouseUp += new MouseEventHandler (OnMouseUpSB);
247 base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
248 base.Resize += new EventHandler (OnResizeSB);
249 base.TabStop = false;
250 base.Cursor = Cursors.Default;
252 SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
254 | ControlStyles.UseTextForAccessibility
259 #region Internal & Private Properties
260 internal Rectangle FirstArrowArea {
262 return this.first_arrow_area;
266 this.first_arrow_area = value;
270 internal Rectangle SecondArrowArea {
272 return this.second_arrow_area;
276 this.second_arrow_area = value;
282 return use_manual_thumb_size ? maximum - manual_thumb_size + 1 :
283 maximum - LargeChange + 1;
287 internal Rectangle ThumbPos {
297 internal bool FirstButtonEntered {
298 get { return first_button_entered; }
300 if (first_button_entered == value)
302 first_button_entered = value;
303 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
304 Invalidate (first_arrow_area);
308 internal bool SecondButtonEntered {
309 get { return second_button_entered; }
311 if (second_button_entered == value)
313 second_button_entered = value;
314 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
315 Invalidate (second_arrow_area);
319 internal bool ThumbEntered {
320 get { return thumb_entered; }
322 if (thumb_entered == value)
324 thumb_entered = value;
325 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
326 Invalidate (thumb_pos);
330 internal bool ThumbPressed {
331 get { return thumb_pressed; }
333 if (thumb_pressed == value)
335 thumb_pressed = value;
336 if (ThemeEngine.Current.ScrollBarHasPressedThumbStyle)
337 Invalidate (thumb_pos);
341 #endregion // Internal & Private Properties
343 #region Public Properties
345 [EditorBrowsable (EditorBrowsableState.Never)]
347 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
348 public override bool AutoSize {
349 get { return base.AutoSize; }
350 set { base.AutoSize = value; }
354 [EditorBrowsable (EditorBrowsableState.Never)]
356 public override Color BackColor
358 get { return base.BackColor; }
360 if (base.BackColor == value)
362 base.BackColor = value;
367 [EditorBrowsable (EditorBrowsableState.Never)]
369 public override Image BackgroundImage
371 get { return base.BackgroundImage; }
373 if (base.BackgroundImage == value)
376 base.BackgroundImage = value;
381 [EditorBrowsable (EditorBrowsableState.Never)]
383 public override ImageLayout BackgroundImageLayout {
384 get { return base.BackgroundImageLayout; }
385 set { base.BackgroundImageLayout = value; }
389 protected override CreateParams CreateParams
391 get { return base.CreateParams; }
395 protected override Padding DefaultMargin {
396 get { return Padding.Empty; }
400 protected override ImeMode DefaultImeMode
402 get { return ImeMode.Disable; }
405 [EditorBrowsable (EditorBrowsableState.Never)]
407 public override Font Font
409 get { return base.Font; }
411 if (base.Font.Equals (value))
418 [EditorBrowsable (EditorBrowsableState.Never)]
420 public override Color ForeColor
422 get { return base.ForeColor; }
424 if (base.ForeColor == value)
427 base.ForeColor = value;
432 [EditorBrowsable (EditorBrowsableState.Never)]
434 public new ImeMode ImeMode
436 get { return base.ImeMode; }
438 if (base.ImeMode == value)
441 base.ImeMode = value;
446 [RefreshProperties(RefreshProperties.Repaint)]
447 [MWFDescription("Scroll amount when clicking in the scroll area"), MWFCategory("Behaviour")]
448 public int LargeChange {
449 get { return Math.Min (large_change, maximum - minimum + 1); }
453 throw new ArgumentOutOfRangeException ("LargeChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
455 throw new ArgumentException( string.Format("Value '{0}' must be greater than or equal to 0.", value));
458 if (large_change != value) {
459 large_change = value;
461 // thumb area depends on large change value,
462 // so we need to recalculate it.
464 UpdatePos (Value, true);
467 // UIA Framework: Generate UIA Event to indicate LargeChange change
468 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.LargeIncrement, value));
475 [RefreshProperties(RefreshProperties.Repaint)]
476 [MWFDescription("Highest value for scrollbar"), MWFCategory("Behaviour")]
478 get { return maximum; }
480 if (maximum == value)
486 // UIA Framework: Generate UIA Event to indicate Maximum change
487 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.Last, value));
490 if (maximum < minimum)
495 // thumb area depends on maximum value,
496 // so we need to recalculate it.
498 UpdatePos (Value, true);
503 internal void SetValues (int maximum, int large_change)
505 SetValues (-1, maximum, -1, large_change);
508 internal void SetValues (int minimum, int maximum, int small_change, int large_change)
512 if (-1 != minimum && this.minimum != minimum) {
513 this.minimum = minimum;
515 if (minimum > this.maximum)
516 this.maximum = minimum;
519 // change the position if it is out of range now
520 position = Math.Max (position, minimum);
523 if (-1 != maximum && this.maximum != maximum) {
524 this.maximum = maximum;
526 if (maximum < this.minimum)
527 this.minimum = maximum;
530 // change the position if it is out of range now
531 position = Math.Min (position, maximum);
534 if (-1 != small_change && this.small_change != small_change) {
535 this.small_change = small_change;
538 if (this.large_change != large_change) {
539 this.large_change = large_change;
545 UpdatePos (Value, true);
551 [RefreshProperties(RefreshProperties.Repaint)]
552 [MWFDescription("Smallest value for scrollbar"), MWFCategory("Behaviour")]
554 get { return minimum; }
556 if (minimum == value)
562 // UIA Framework: Generate UIA Event to indicate Minimum change
563 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.First, value));
566 if (minimum > maximum)
569 // thumb area depends on minimum value,
570 // so we need to recalculate it.
572 UpdatePos (Value, true);
578 [MWFDescription("Scroll amount when clicking scroll arrows"), MWFCategory("Behaviour")]
579 public int SmallChange {
580 get { return small_change > LargeChange ? LargeChange : small_change; }
584 throw new ArgumentOutOfRangeException ("SmallChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
586 throw new ArgumentException( string.Format("Value '{0}' must be greater than or equal to 0.", value));
589 if (small_change != value) {
590 small_change = value;
591 UpdatePos (Value, true);
594 // UIA Framework: Generate UIA Event to indicate SmallChange change
595 OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.SmallIncrement, value));
601 [DefaultValue (false)]
602 public new bool TabStop {
603 get { return base.TabStop; }
604 set { base.TabStop = value; }
607 [EditorBrowsable (EditorBrowsableState.Never)]
610 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
611 public override string Text {
612 get { return base.Text; }
613 set { base.Text = value; }
618 [MWFDescription("Current value for scrollbar"), MWFCategory("Behaviour")]
620 get { return position; }
622 if ( value < minimum || value > maximum )
624 throw new ArgumentOutOfRangeException ("Value", string.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
626 throw new ArgumentException (string.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
629 if (position != value){
632 OnValueChanged (EventArgs.Empty);
634 if (IsHandleCreated) {
635 Rectangle thumb_rect = thumb_pos;
637 UpdateThumbPos ((vert ? thumb_area.Y : thumb_area.X) + (int)(((float)(position - minimum)) * pixel_per_pos), false, false);
639 MoveThumb (thumb_rect, vert ? thumb_pos.Y : thumb_pos.X);
645 #endregion //Public Properties
647 #region Public Methods
649 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
651 // Basically, we want to keep our small edge and scale the long edge
652 // ie: if we are vertical, don't scale our width
654 return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Height) | (specified & BoundsSpecified.Location));
656 return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Width) | (specified & BoundsSpecified.Location));
660 protected override void OnEnabledChanged (EventArgs e)
662 base.OnEnabledChanged (e);
665 firstbutton_state = secondbutton_state = ButtonState.Normal;
667 firstbutton_state = secondbutton_state = ButtonState.Inactive;
672 protected override void OnHandleCreated (System.EventArgs e)
674 base.OnHandleCreated (e);
678 UpdateThumbPos (thumb_area.Y + (int)(((float)(position - minimum)) * pixel_per_pos), true, false);
681 protected virtual void OnScroll (ScrollEventArgs se)
683 ScrollEventHandler eh = (ScrollEventHandler)(Events [ScrollEvent]);
687 if (se.NewValue < Minimum) {
688 se.NewValue = Minimum;
691 if (se.NewValue > Maximum) {
692 se.NewValue = Maximum;
698 private void SendWMScroll(ScrollBarCommands cmd) {
699 if ((Parent != null) && Parent.IsHandleCreated) {
701 XplatUI.SendMessage(Parent.Handle, Msg.WM_VSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
703 XplatUI.SendMessage(Parent.Handle, Msg.WM_HSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
708 protected virtual void OnValueChanged (EventArgs e)
710 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
715 public override string ToString()
717 return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
718 GetType( ).FullName, minimum, maximum, position);
721 protected void UpdateScrollInfo ()
726 protected override void WndProc (ref Message m)
728 base.WndProc (ref m);
731 #endregion //Public Methods
733 #region Private Methods
735 private void CalcButtonSizes ()
738 if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
739 scrollbutton_height = Height /2;
741 scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
744 if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
745 scrollbutton_width = Width /2;
747 scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
751 private void CalcThumbArea ()
753 int lchange = use_manual_thumb_size ? manual_thumb_size : LargeChange;
758 thumb_area.Height = Height - scrollbutton_height - scrollbutton_height;
760 thumb_area.Y = scrollbutton_height;
761 thumb_area.Width = Width;
763 if (Height < thumb_notshown_size)
766 double per = ((double) lchange / (double)((1 + maximum - minimum)));
767 thumb_size = 1 + (int) (thumb_area.Height * per);
769 if (thumb_size < thumb_min_size)
770 thumb_size = thumb_min_size;
772 // Give the user something to drag if LargeChange is zero
773 if (LargeChange == 0)
777 pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
782 thumb_area.X = scrollbutton_width;
783 thumb_area.Height = Height;
784 thumb_area.Width = Width - scrollbutton_width - scrollbutton_width;
786 if (Width < thumb_notshown_size)
789 double per = ((double) lchange / (double)((1 + maximum - minimum)));
790 thumb_size = 1 + (int) (thumb_area.Width * per);
792 if (thumb_size < thumb_min_size)
793 thumb_size = thumb_min_size;
795 // Give the user something to drag if LargeChange is zero
796 if (LargeChange == 0)
800 pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
804 private void LargeIncrement ()
806 ScrollEventArgs event_args;
807 int pos = Math.Min (MaximumAllowed, position + large_change);
809 event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
810 OnScroll (event_args);
811 Value = event_args.NewValue;
813 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
814 OnScroll (event_args);
815 Value = event_args.NewValue;
818 // UIA Framework event invoked when the "LargeIncrement
819 // Button" is "clicked" either by using the Invoke Pattern
820 // or the space between the thumb and the bottom/right button
821 OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeIncrement, Value));
825 private void LargeDecrement ()
827 ScrollEventArgs event_args;
828 int pos = Math.Max (Minimum, position - large_change);
830 event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
831 OnScroll (event_args);
832 Value = event_args.NewValue;
834 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
835 OnScroll (event_args);
836 Value = event_args.NewValue;
839 // UIA Framework event invoked when the "LargeDecrement
840 // Button" is "clicked" either by using the Invoke Pattern
841 // or the space between the thumb and the top/left button
842 OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeDecrement, Value));
846 private void OnResizeSB (Object o, EventArgs e)
848 if (Width <= 0 || Height <= 0)
853 UpdatePos (position, true);
858 internal override void OnPaintInternal (PaintEventArgs pevent)
860 ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
863 private void OnTimer (Object source, EventArgs e)
867 switch (timer_type) {
869 case TimerType.HoldButton:
870 SetRepeatButtonTimer ();
873 case TimerType.RepeatButton:
875 if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Minimum) {
877 SendWMScroll(ScrollBarCommands.SB_LINEUP);
880 if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Maximum) {
882 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
888 case TimerType.HoldThumbArea:
889 SetRepeatThumbAreaTimer ();
892 case TimerType.RepeatThumbArea:
894 Point pnt, pnt_screen;
895 Rectangle thumb_area_screen = thumb_area;
897 pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
898 thumb_area_screen.X = pnt_screen.X;
899 thumb_area_screen.Y = pnt_screen.Y;
901 if (thumb_area_screen.Contains (MousePosition) == false) {
902 timer.Enabled = false;
903 thumb_moving = ThumbMoving.None;
908 pnt = PointToClient (MousePosition);
911 lastclick_pos = pnt.Y;
913 lastclick_pos = pnt.X;
915 if (thumb_moving == ThumbMoving.Forward) {
916 if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
917 (!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
918 (thumb_area.Contains (pnt) == false)) {
919 timer.Enabled = false;
920 thumb_moving = ThumbMoving.None;
925 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
928 if ((vert && (thumb_pos.Y < lastclick_pos)) ||
929 (!vert && (thumb_pos.X < lastclick_pos))){
930 timer.Enabled = false;
931 thumb_moving = ThumbMoving.None;
932 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
936 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
949 private void MoveThumb (Rectangle original_thumbpos, int value)
951 /* so, the reason this works can best be
952 * described by the following 1 dimensional
955 * say you have a scrollbar thumb positioned
958 * <---------------------| |------------------------------>
960 * and you want it to end up looking like this:
962 * <-----------------------------| |---------------------->
964 * that can be done with the scrolling api by
965 * extending the rectangle to encompass both
968 * start of range end of range
970 * <---------------------| |-------|---------------------->
972 * so, we end up scrolling just this little region:
976 * and end up with ********| |
978 * where ****** is space that is automatically
981 * It's clear that in both cases (left to
982 * right, right to left) we need to extend the
983 * size of the scroll rectangle to encompass
984 * both. In the right to left case, we also
985 * need to decrement the X coordinate.
987 * We call Update after scrolling to make sure
988 * there's no garbage left in the window to be
989 * copied again if we're called before the
990 * paint events have been handled.
996 delta = value - original_thumbpos.Y;
999 original_thumbpos.Y += delta;
1000 original_thumbpos.Height -= delta;
1003 original_thumbpos.Height += delta;
1006 XplatUI.ScrollWindow (Handle, original_thumbpos, 0, delta, false);
1009 delta = value - original_thumbpos.X;
1012 original_thumbpos.X += delta;
1013 original_thumbpos.Width -= delta;
1016 original_thumbpos.Width += delta;
1019 XplatUI.ScrollWindow (Handle, original_thumbpos, delta, 0, false);
1025 private void OnMouseMoveSB (object sender, MouseEventArgs e)
1027 if (Enabled == false)
1030 FirstButtonEntered = first_arrow_area.Contains (e.Location);
1031 SecondButtonEntered = second_arrow_area.Contains (e.Location);
1033 if (thumb_size == 0)
1036 ThumbEntered = thumb_pos.Contains (e.Location);
1038 if (firstbutton_pressed) {
1039 if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
1040 firstbutton_state = ButtonState.Normal;
1041 Invalidate (first_arrow_area);
1044 } else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
1045 firstbutton_state = ButtonState.Pushed;
1046 Invalidate (first_arrow_area);
1050 } else if (secondbutton_pressed) {
1051 if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
1052 secondbutton_state = ButtonState.Normal;
1053 Invalidate (second_arrow_area);
1056 } else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
1057 secondbutton_state = ButtonState.Pushed;
1058 Invalidate (second_arrow_area);
1062 } else if (thumb_pressed == true) {
1064 int thumb_edge = e.Y - thumbclick_offset;
1066 if (thumb_edge < thumb_area.Y)
1067 thumb_edge = thumb_area.Y;
1068 else if (thumb_edge > thumb_area.Bottom - thumb_size)
1069 thumb_edge = thumb_area.Bottom - thumb_size;
1071 if (thumb_edge != thumb_pos.Y) {
1072 Rectangle thumb_rect = thumb_pos;
1074 UpdateThumbPos (thumb_edge, false, true);
1076 MoveThumb (thumb_rect, thumb_pos.Y);
1078 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
1080 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1082 int thumb_edge = e.X - thumbclick_offset;
1084 if (thumb_edge < thumb_area.X)
1085 thumb_edge = thumb_area.X;
1086 else if (thumb_edge > thumb_area.Right - thumb_size)
1087 thumb_edge = thumb_area.Right - thumb_size;
1089 if (thumb_edge != thumb_pos.X) {
1090 Rectangle thumb_rect = thumb_pos;
1092 UpdateThumbPos (thumb_edge, false, true);
1094 MoveThumb (thumb_rect, thumb_pos.X);
1096 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
1098 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1105 private void OnMouseDownSB (object sender, MouseEventArgs e)
1109 if (Enabled == false || (e.Button & MouseButtons.Left) == 0)
1112 if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
1113 SendWMScroll(ScrollBarCommands.SB_LINEUP);
1114 firstbutton_state = ButtonState.Pushed;
1115 firstbutton_pressed = true;
1116 Invalidate (first_arrow_area);
1118 if (!timer.Enabled) {
1119 SetHoldButtonClickTimer ();
1120 timer.Enabled = true;
1124 if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
1125 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1126 secondbutton_state = ButtonState.Pushed;
1127 secondbutton_pressed = true;
1128 Invalidate (second_arrow_area);
1130 if (!timer.Enabled) {
1131 SetHoldButtonClickTimer ();
1132 timer.Enabled = true;
1136 if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
1137 ThumbPressed = true;
1138 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1140 thumbclick_offset = e.Y - thumb_pos.Y;
1141 lastclick_pos = e.Y;
1144 thumbclick_offset = e.X - thumb_pos.X;
1145 lastclick_pos = e.X;
1148 if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
1151 lastclick_pos = e.Y;
1153 if (e.Y > thumb_pos.Y + thumb_pos.Height) {
1154 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1156 thumb_moving = ThumbMoving.Forward;
1157 Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1158 ClientRectangle.Width,
1159 ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
1160 scrollbutton_height));
1162 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1164 thumb_moving = ThumbMoving.Backwards;
1165 Dirty (new Rectangle (0, scrollbutton_height,
1166 ClientRectangle.Width,
1167 thumb_pos.Y - scrollbutton_height));
1171 lastclick_pos = e.X;
1173 if (e.X > thumb_pos.X + thumb_pos.Width) {
1174 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1175 thumb_moving = ThumbMoving.Forward;
1177 Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1178 ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
1180 ClientRectangle.Height));
1182 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1183 thumb_moving = ThumbMoving.Backwards;
1185 Dirty (new Rectangle (scrollbutton_width, 0,
1186 thumb_pos.X - scrollbutton_width,
1187 ClientRectangle.Height));
1191 SetHoldThumbAreaTimer ();
1192 timer.Enabled = true;
1198 private void OnMouseUpSB (object sender, MouseEventArgs e)
1202 if (Enabled == false)
1205 timer.Enabled = false;
1206 if (thumb_moving != ThumbMoving.None) {
1208 thumb_moving = ThumbMoving.None;
1211 if (firstbutton_pressed) {
1212 firstbutton_state = ButtonState.Normal;
1213 if (first_arrow_area.Contains (e.X, e.Y)) {
1216 SendWMScroll(ScrollBarCommands.SB_LINEUP);
1217 firstbutton_pressed = false;
1218 Dirty (first_arrow_area);
1219 } else if (secondbutton_pressed) {
1220 secondbutton_state = ButtonState.Normal;
1221 if (second_arrow_area.Contains (e.X, e.Y)) {
1224 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1225 Dirty (second_arrow_area);
1226 secondbutton_pressed = false;
1227 } else if (thumb_pressed == true) {
1228 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
1229 OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
1230 SendWMScroll(ScrollBarCommands.SB_THUMBPOSITION);
1231 ThumbPressed = false;
1238 private void OnKeyDownSB (Object o, KeyEventArgs key)
1240 if (Enabled == false)
1245 switch (key.KeyCode){
1283 // I hate to do this, but we don't have the resources to track
1284 // down everything internal that is setting a value outside the
1285 // correct range, so we'll clamp it to the acceptable values.
1286 internal void SafeValueSet (int value)
1288 value = Math.Min (value, maximum);
1289 value = Math.Max (value, minimum);
1294 private void SetEndPosition ()
1296 ScrollEventArgs event_args;
1297 int pos = MaximumAllowed;
1299 event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
1300 OnScroll (event_args);
1301 pos = event_args.NewValue;
1303 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1304 OnScroll (event_args);
1305 pos = event_args.NewValue;
1310 private void SetHomePosition ()
1312 ScrollEventArgs event_args;
1315 event_args = new ScrollEventArgs (ScrollEventType.First, pos);
1316 OnScroll (event_args);
1317 pos = event_args.NewValue;
1319 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1320 OnScroll (event_args);
1321 pos = event_args.NewValue;
1326 private void SmallIncrement ()
1328 ScrollEventArgs event_args;
1329 int pos = Math.Min (MaximumAllowed, position + SmallChange);
1331 event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
1332 OnScroll (event_args);
1333 Value = event_args.NewValue;
1335 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1336 OnScroll (event_args);
1337 Value = event_args.NewValue;
1340 // UIA Framework event invoked when the "SmallIncrement
1341 // Button" (a.k.a bottom/right button) is "clicked" either
1342 // by using the Invoke Pattern or the button itself
1343 OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallIncrement, Value));
1347 private void SmallDecrement ()
1349 ScrollEventArgs event_args;
1350 int pos = Math.Max (Minimum, position - SmallChange);
1352 event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
1353 OnScroll (event_args);
1354 Value = event_args.NewValue;
1356 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1357 OnScroll (event_args);
1358 Value = event_args.NewValue;
1361 // UIA Framework event invoked when the "SmallDecrement
1362 // Button" (a.k.a top/left button) is "clicked" either
1363 // by using the Invoke Pattern or the button itself
1364 OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallDecrement, Value));
1368 private void SetHoldButtonClickTimer ()
1370 timer.Enabled = false;
1371 timer.Interval = 200;
1372 timer_type = TimerType.HoldButton;
1373 timer.Enabled = true;
1376 private void SetRepeatButtonTimer ()
1378 timer.Enabled = false;
1379 timer.Interval = 50;
1380 timer_type = TimerType.RepeatButton;
1381 timer.Enabled = true;
1384 private void SetHoldThumbAreaTimer ()
1386 timer.Enabled = false;
1387 timer.Interval = 200;
1388 timer_type = TimerType.HoldThumbArea;
1389 timer.Enabled = true;
1392 private void SetRepeatThumbAreaTimer ()
1394 timer.Enabled = false;
1395 timer.Interval = 50;
1396 timer_type = TimerType.RepeatThumbArea;
1397 timer.Enabled = true;
1400 private void UpdatePos (int newPos, bool update_thumbpos)
1404 if (newPos < minimum)
1407 if (newPos > MaximumAllowed)
1408 pos = MaximumAllowed;
1412 // pos can't be less than minimum or greater than maximum
1418 if (update_thumbpos) {
1420 UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1422 UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1426 position = pos; // Updates directly the value to avoid thumb pos update
1429 // XXX some reason we don't call OnValueChanged?
1430 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
1432 eh (this, EventArgs.Empty);
1436 private void UpdateThumbPos (int pixel, bool dirty, bool update_value)
1443 if (pixel < thumb_area.Y)
1444 thumb_pos.Y = thumb_area.Y;
1445 else if (pixel > thumb_area.Bottom - thumb_size)
1446 thumb_pos.Y = thumb_area.Bottom - thumb_size;
1448 thumb_pos.Y = pixel;
1451 thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
1452 thumb_pos.Height = thumb_size;
1453 new_pos = (float) (thumb_pos.Y - thumb_area.Y);
1454 new_pos = new_pos / pixel_per_pos;
1460 if (pixel < thumb_area.X)
1461 thumb_pos.X = thumb_area.X;
1462 else if (pixel > thumb_area.Right - thumb_size)
1463 thumb_pos.X = thumb_area.Right - thumb_size;
1465 thumb_pos.X = pixel;
1468 thumb_pos.Width = thumb_size;
1469 thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
1470 new_pos = (float) (thumb_pos.X - thumb_area.X);
1471 new_pos = new_pos / pixel_per_pos;
1478 UpdatePos ((int) new_pos + minimum, false);
1481 private void SetValue (int value)
1483 if ( value < minimum || value > maximum )
1484 throw new ArgumentException(
1485 String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
1487 if (position != value){
1490 OnValueChanged (EventArgs.Empty);
1491 UpdatePos (value, true);
1495 private void ClearDirty ()
1497 dirty = Rectangle.Empty;
1500 private void Dirty (Rectangle r)
1502 if (dirty == Rectangle.Empty) {
1506 dirty = Rectangle.Union (dirty, r);
1509 private void DirtyThumbArea ()
1511 if (thumb_moving == ThumbMoving.Forward) {
1513 Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1514 ClientRectangle.Width,
1515 ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
1516 scrollbutton_height));
1518 Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1519 ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
1521 ClientRectangle.Height));
1523 } else if (thumb_moving == ThumbMoving.Backwards) {
1525 Dirty(new Rectangle (0, scrollbutton_height,
1526 ClientRectangle.Width,
1527 thumb_pos.Y - scrollbutton_height));
1529 Dirty (new Rectangle (scrollbutton_width, 0,
1530 thumb_pos.X - scrollbutton_width,
1531 ClientRectangle.Height));
1536 private void InvalidateDirty ()
1540 dirty = Rectangle.Empty;
1543 void OnMouseEnter (object sender, EventArgs e)
1545 if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
1546 Region region_to_invalidate = new Region (first_arrow_area);
1547 region_to_invalidate.Union (second_arrow_area);
1548 Invalidate (region_to_invalidate);
1552 void OnMouseLeave (object sender, EventArgs e)
1554 Region region_to_invalidate = new Region ();
1555 region_to_invalidate.MakeEmpty ();
1557 if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
1558 region_to_invalidate.Union (first_arrow_area);
1559 region_to_invalidate.Union (second_arrow_area);
1562 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
1563 if (first_button_entered) {
1564 region_to_invalidate.Union (first_arrow_area);
1566 } else if (second_button_entered) {
1567 region_to_invalidate.Union (second_arrow_area);
1570 if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
1571 if (thumb_entered) {
1572 region_to_invalidate.Union (thumb_pos);
1575 first_button_entered = false;
1576 second_button_entered = false;
1577 thumb_entered = false;
1579 Invalidate (region_to_invalidate);
1580 region_to_invalidate.Dispose ();
1582 #endregion //Private Methods
1584 protected override void OnMouseWheel (MouseEventArgs e)
1586 base.OnMouseWheel (e);
1590 #region UIA Framework Section: Events, Methods and Properties.
1594 // We are using Reflection to add/remove internal events.
1595 // Class ScrollBarButtonInvokePatternInvokeEvent uses the events.
1597 // Types used to generate UIA InvokedEvent
1598 // * args.Type = ScrollEventType.LargeIncrement. Space between Thumb and bottom/right Button
1599 // * args.Type = ScrollEventType.LargeDecrement. Space between Thumb and top/left Button
1600 // * args.Type = ScrollEventType.SmallIncrement. Small increment UIA Button (bottom/right Button)
1601 // * args.Type = ScrollEventType.SmallDecrement. Small decrement UIA Button (top/left Button)
1602 // Types used to generate RangeValue-related events
1603 // * args.Type = ScrollEventType.LargeIncrement. LargeChange event
1604 // * args.Type = ScrollEventType.Last. Maximum event
1605 // * args.Type = ScrollEventType.First. Minimum event
1606 // * args.Type = ScrollEventType.SmallIncrement. SmallChange event
1607 static object UIAScrollEvent = new object ();
1608 static object UIAValueChangeEvent = new object ();
1610 internal event ScrollEventHandler UIAScroll {
1611 add { Events.AddHandler (UIAScrollEvent, value); }
1612 remove { Events.RemoveHandler (UIAScrollEvent, value); }
1615 internal event ScrollEventHandler UIAValueChanged {
1616 add { Events.AddHandler (UIAValueChangeEvent, value); }
1617 remove { Events.RemoveHandler (UIAValueChangeEvent, value); }
1620 internal void OnUIAScroll (ScrollEventArgs args)
1622 ScrollEventHandler eh = (ScrollEventHandler) Events [UIAScrollEvent];
1627 internal void OnUIAValueChanged (ScrollEventArgs args)
1629 ScrollEventHandler eh = (ScrollEventHandler) Events [UIAValueChangeEvent];
1635 // Wrapper methods used by the Reflection.
1636 // Class ScrollBarButtonInvokeProviderBehavior uses the events.
1638 internal void UIALargeIncrement ()
1643 internal void UIALargeDecrement ()
1648 internal void UIASmallIncrement ()
1653 internal void UIASmallDecrement ()
1658 internal Rectangle UIAThumbArea {
1659 get { return thumb_area; }
1662 internal Rectangle UIAThumbPosition {
1663 get { return thumb_pos; }
1668 #endregion UIA Framework Section: Events, Methods and Properties.