@ EvoMotors - Keep in mind, this is not written for Glide so you will need to do some alteration.
Notes:
- If scrolling is needed it will automatically display the scrollbar for 1 second and then hide it.
- When you scroll (just by touchmove) scrollbar(s) will again automatically appear and disappear after you let go.
- Scroll needs are determined by RequiredHeight/Width and AvailableHeight/Width.
- Available height/width is the actual size of the control
- Required height/width is the area needed to display the entire contents, it is up to you to set these values.
using System;
using System.Threading;
using Microsoft.SPOT;
namespace Skewworks.Artifact
{
[Serializable]
public class ScrollableControl : Control
{
#region Variables
// Scrolling
private int _rqWidth;
private int _rqHeight;
private int _scrX, _scrY;
private bool _needsX, _needsY;
private int _sqW, _sqH;
internal bool _showScroll;
private bool _bMoving;
private int _horzW, _horzSize, _horzX;
private int _vertH, _vertSize, _vertY;
private Thread _thAutoHide;
private long _killTime;
#endregion
#region Properties
public int MaxScrollX
{
get { return _rqWidth; }
}
public int MaxScrollY
{
get { return _rqHeight; }
}
protected int RequiredHeight
{
get { return _rqHeight; }
set
{
if (_rqHeight == value)
return;
_rqHeight = value;
UpdateNeeds();
}
}
protected int RequiredWidth
{
get { return _rqWidth; }
set
{
if (_rqWidth == value)
return;
_rqWidth = value;
UpdateNeeds();
}
}
public bool Scrolling
{
get { return _bMoving; }
}
public virtual int ScrollX
{
get { return _scrX; }
set
{
if (value < 0)
value = 0;
else if (value > _sqW)
value = _sqW;
if (!_needsX || _scrX == value)
return;
_scrX = value;
SetAutoHide();
Invalidate();
}
}
public virtual int ScrollY
{
get { return _scrY; }
set
{
if (value < 0)
value = 0;
else if (value > _sqH)
value = _sqH;
if (!_needsY || _scrY == value)
return;
_scrY = value;
SetAutoHide();
Invalidate();
}
}
#endregion
#region Touch & Mouse
public override void MouseMove (Point pt, ref bool handled)
{
if (HIDMove(pt, ref handled))
base.MouseMove(pt, ref handled);
}
public override void TouchMove (Point pt, ref bool handled)
{
if (HIDMove(pt, ref handled))
base.TouchMove(pt, ref handled);
}
private bool HIDMove (Point e, ref bool handled)
{
if (Touching || MouseDown)
{
if (_needsX || _needsY)
{
bool doInvalidate = false;
SetAutoHide();
int dest = _scrY - (e.Y - LastTouch.Y);
if (dest < 0)
dest = 0;
else if (dest > _rqHeight - Height)
dest = _rqHeight - Height;
if (_scrY != dest && dest >= 0)
{
_scrY = dest;
doInvalidate = true;
}
dest = _scrX - (e.X - LastTouch.X);
if (dest < 0)
dest = 0;
else if (dest > _rqWidth - Width)
dest = _rqWidth - Width;
if (_scrX != dest && dest > 0)
{
_scrX = dest;
doInvalidate = true;
}
handled = true;
LastTouch = e;
if (doInvalidate)
{
_bMoving = true;
UpdateValues();
}
else
return true;
}
else
return true;
}
return false;
}
public override void TouchUp (Point pt, ref bool handled)
{
base.TouchUp(pt, ref handled);
_bMoving = false;
}
#endregion
#region Scrollbar Methods
public override void Render (Bitmap dest, int x, int y, int width, int height, bool flush = false)
{
if (_showScroll)
RenderScrollbar(dest, x, y, width, height);
if (flush)
Kernel.Flush(dest, x, y, width, height);
}
private void AutoHideScrollbar ()
{
while (true)
{
Thread.Sleep(100);
if (DateTime.Now.Ticks >= _killTime)
{
_showScroll = false;
Invalidate();
break;
}
}
_thAutoHide = null;
}
internal void RenderScrollbar (Bitmap dest, int x, int y, int width, int height)
{
if (_needsX)
{
int y2 = y + height - 4;
dest.DrawRectangle(0, 0, x, y2, _horzW, 4, 0, 0, Colors.Black, 0, 0, Colors.Black, 0, 0, 128);
dest.DrawRectangle(0, 0, x + _horzX, y2, _horzSize, 4, 0, 0, Colors.White, 0, 0, Colors.White, 0, 0, 128);
}
if (_needsY)
{
int x2 = x + width - 4;
dest.DrawRectangle(0, 0, x2, y, 4, _vertH, 0, 0, Colors.Black, 0, 0, Colors.Black, 0, 0, 128);
dest.DrawRectangle(0, 0, x2, y + _vertY, 4, _vertSize, 0, 0, Colors.White, 0, 0, Colors.White, 0, 0, 128);
}
}
protected void UpdateScrollbar (bool invalidate = true)
{
UpdateNeeds(invalidate);
}
private void UpdateNeeds (bool invalidate = true)
{
bool nX = false;
bool nY = false;
if (_rqWidth > Width)
nX = true;
if (_rqHeight > Height)
nY = true;
if (nX || nY)
{
_needsX = nX;
_needsY = nY;
_sqW = _rqWidth - Width;
_sqH = _rqHeight - Height;
SetAutoHide();
UpdateValues(invalidate);
}
}
private void UpdateValues (bool invalidate = true)
{
int w = Width;
int h = Height;
if (_needsX && _needsY)
{
w -= 4;
h -= 4;
}
if (_needsX)
{
_horzW = w;
_horzSize = _horzW - (_sqW / 4);
if (_horzSize < 8)
_horzSize = 8;
_horzX = (int) ((_horzW - _horzSize) * ((float) _scrX / (float) _sqW));
}
if (_needsY)
{
_vertH = h;
_vertSize = _vertH - (_sqH / 4);
if (_vertSize < 8)
_vertSize = 8;
_vertY = (int) ((_vertH - _vertSize) * ((float) _scrY / (float) _sqH));
}
if (invalidate)
Invalidate();
}
private void SetAutoHide ()
{
if (!_showScroll)
_showScroll = true;
_killTime = DateTime.Now.Ticks + TimeSpan.TicksPerSecond;
UpdateValues(false);
if (_thAutoHide != null && _thAutoHide.IsAlive)
return;
_thAutoHide = new Thread(AutoHideScrollbar);
_thAutoHide.Start();
}
#endregion
}
}
Example control deriving from ScrollableControl
using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation.Media;
namespace Skewworks.Artifact
{
[Serializable]
public class ListBox : ScrollableControl
{
#region Variables
private ListItem[] _items;
private bool _showChk, _showImg, _zebra;
private int _selIndex, _lineHeight;
private Font _font;
private Color _brdr, _bkg, _fore, _selBkg, _selFore;
#endregion
#region Constructors
public ListBox (string name, Font font, int x, int y, int width, int height)
: base()
{
Name = name;
_font = font;
Defaults();
Bounds = new Rect(x, y, width, height);
UpdateLayout();
}
public ListBox (string name, Font font, Alignment align)
: base()
{
Name = name;
_font = font;
this.Align = align;
Defaults();
UpdateLayout();
}
public ListBox (string name, Font font, Alignment align, Margin margin)
: base()
{
Name = name;
_font = font;
this.Align = align;
this.Margin = margin;
Defaults();
UpdateLayout();
}
#endregion
#region Events
public event OnSelectedIndexChanged SelectedIndexChanged;
protected virtual void OnSelectedIndexChanged (object sender, int index)
{
if (SelectedIndexChanged != null)
SelectedIndexChanged(sender, index);
}
#endregion
#region Properties
public Font Font
{
get { return _font; }
set
{
if (_font == value)
return;
_font = value;
UpdateLayout();
}
}
public ListItem[] Items
{
get { return _items; }
set
{
if (_items == value)
return;
_items = value;
UpdateLayout();
}
}
public int LineHeight
{
get { return _lineHeight; }
set
{
if (_lineHeight == value)
return;
_lineHeight = value;
UpdateLayout();
}
}
public int SelectedIndex
{
get { return _selIndex; }
set
{
if (_items == null)
return;
if (value > _items.Length - 1)
value = _items.Length - 1;
if (_selIndex == value)
return;
_selIndex = value;
if (!ShowSelected())
Invalidate();
OnSelectedIndexChanged(this, _selIndex);
}
}
public bool ShowCheckBoxes
{
get { return _showChk; }
set
{
if (_showChk == value)
return;
_showChk = value;
UpdateLayout();
}
}
public bool ShowImages
{
get { return _showImg; }
set
{
if (_showImg == value)
return;
_showImg = value;
UpdateLayout();
}
}
public bool ZebraStripes
{
get { return _zebra; }
set
{
if (_zebra == value)
return;
_zebra = value;
Invalidate();
}
}
#endregion
#region Public Methods
public void AddItem (ListItem item)
{
// Update Array Size
if (_items == null)
_items = new ListItem[] { item };
else
{
ListItem[] tmp = new ListItem[_items.Length + 1];
Array.Copy(_items, tmp, _items.Length);
tmp[tmp.Length - 1] = item;
_items = tmp;
tmp = null;
}
UpdateLayout();
Invalidate();
}
public void AddItems (ListItem[] items)
{
if (items == null)
return;
this.Suspended = true;
if (_items == null)
_items = items;
else
{
ListItem[] tmp = new ListItem[_items.Length + items.Length];
Array.Copy(_items, tmp, _items.Length);
Array.Copy(items, 0, tmp, _items.Length, items.Length);
_items = tmp;
}
UpdateLayout();
this.Suspended = false;
}
public void ClearItems ()
{
if (_items == null)
return;
_items = null;
_selIndex = -1;
UpdateLayout();
Invalidate();
}
public void RemoveItem (ListItem item)
{
if (_items == null)
return;
for (int i = 0; i < _items.Length; i++)
{
if (_items[i] == item)
{
RemoveItemAt(i);
return;
}
}
}
public void RemoveItemAt (int index)
{
if (_items == null || index < 0 || index >= _items.Length)
return;
if (_items.Length == 1)
{
ClearItems();
return;
}
this.Suspended = true;
ListItem[] tmp = new ListItem[_items.Length - 1];
int c = 0;
for (int i = 0; i < _items.Length; i++)
{
if (i != index)
tmp[c++] = _items[i];
}
_items = tmp;
UpdateLayout();
this.Suspended = false;
}
#endregion
#region Keyboard
public override void KeyboardKey (KeyboardEventArgs e, ref bool handled)
{
if (!e.Pressed)
return;
switch (e.Key)
{
case Artifact.KeyboardKey.DownArrow:
SelectedIndex += 1;
break;
case Artifact.KeyboardKey.UpArrow:
SelectedIndex -= 1;
break;
}
base.KeyboardKey(e, ref handled);
}
#endregion
#region Mouse & Touch
private void HIDDown (Point pt, ref bool handled)
{
if (!ScreenBounds.Contains(pt))
return;
pt.Y += ScrollY - Top;
SelectedIndex = pt.Y / _lineHeight;
handled = true;
}
public override void MouseButton (MouseEventArgs e, ref bool handled)
{
base.MouseButton(e, ref handled);
if (e.Button == Artifact.MouseButton.Left && e.Pressed)
{
HIDDown(e.Location, ref handled);
}
}
public override void MouseWheel (int change)
{
ScrollY -= change;
}
public override void TouchDown (Point pt, ref bool handled)
{
base.TouchDown(pt, ref handled);
HIDDown(pt, ref handled);
}
#endregion
#region GUI
public override void Render (Bitmap dest, int x, int y, int width, int height, bool flush = false)
{
if (!ValidRenderState)
return;
dest.DrawRectangle(_brdr, 1, x, y, width, height, 0, 0, _bkg, 0, 0, _bkg, 0, 0, 256);
if (_items == null)
{
RequiredHeight = 0;
return;
}
dest.SetClippingRectangle(x + 1, y + 1, width - 2, height - 2);
int y2 = y - ScrollY;
int x2;
Color c;
for (int i = 0; i < _items.Length; i++)
{
if (y2 + _lineHeight >= y)
{
x2 = x + 4;
c = _fore;
if (i == _selIndex)
{
c = _selFore;
dest.DrawRectangle(0, 0, x + 1, y2, width - 2, _lineHeight, 0, 0, _selBkg, 0, 0, _selBkg, 0, 0, 256);
}
if (_showChk)
{
dest.DrawRectangle(_brdr, 1, x2, y2 + (_lineHeight / 2) - 8, 16, 16, 0, 0, _bkg, 0, 0, _bkg, 0, 0, 256);
x2 += 20;
}
if (_showImg && _items[i].Image != null)
{
dest.DrawImage(x2, y2 + (_lineHeight / 2 - _items[i].Image.Height / 2), _items[i].Image, 0, 0, _items[i].Image.Width, _items[i].Image.Height);
x2 += _items[i].Image.Width + 4;
}
if (_items[i].Text != null)
dest.DrawText(_items[i].Text, _font, c, x2, y2 + (_lineHeight / 2 - _font.Height / 2));
}
y2 += _lineHeight;
if (y2 >= y + height)
break;
}
RequiredHeight = _lineHeight * _items.Length + 2;
dest.SetClippingRectangle(0, 0, dest.Width, dest.Height);
base.Render(dest, x, y, width, height, flush);
}
#endregion
#region Private Methods
private void Defaults ()
{
_brdr = Colors.DarkGray;
_bkg = Colors.White;
_fore = Colors.CharcoalDust;
_selBkg = Colors.LightBlue;
_selFore = Colors.White;
}
private bool ShowSelected ()
{
int y = (_lineHeight * _selIndex) + _lineHeight - ScrollY;
if (y < _lineHeight)
{
ScrollY += y - _lineHeight;
return true;
}
else if (y > Height)
{
ScrollY += y - Height;
return true;
}
return false;
}
private void UpdateLayout ()
{
int h;
h = _font.Height + 4;
if (_showChk)
h = System.Math.Max(h, 20);
if (_showImg && _items != null)
{
for (int i = 0; i < _items.Length; i++)
{
if (_items[i].Image != null)
h = System.Math.Max(h, _items[i].Image.Height + 4);
}
}
_lineHeight = h;
Invalidate();
}
#endregion
}
}