diff dynamin/gui/scrollable.d @ 0:aa4efef0f0b1

Initial commit of code.
author Jordan Miner <jminer7@gmail.com>
date Mon, 15 Jun 2009 22:10:48 -0500
parents
children 4029d5af7542
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/scrollable.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,308 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.scrollable;
+
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.all_gui;
+import tango.io.Stdout;
+
+///
+enum VisibleScrollBars {
+	/**
+	  * Shows a vertical scroll bar when it is needed. A horizontal scroll bar
+	  * is never shown. A text box with line wrap turned on could use this
+	  * option.
+	  */
+	Vertical,
+	/**
+	  * Shows a horizontal scroll bar when it is needed. A vertical scroll bar
+	  * is never shown. The list view in Windows Explorer could use this option.
+	  */
+	Horizontal,
+	/**
+	 * Shows either scroll bar when it is needed.
+	 * A control, such as the detail view in Windows Explorer, that changes its
+	 * width when not being scrolled should use this option.
+	 */
+	Each,
+	/**
+	  * Shows both scroll bars when both are needed.
+	  * A control, such as a text box, that changes its width based upon
+	  * where it is scrolled to should use this option.
+	  */
+	Both,
+	/**
+	  * Does not show any scroll bars, even if they are needed.
+	  * A single-line text box uses this option.
+	  */
+	None
+}
+
+class ScrollableClipper : Control {
+protected:
+	override void whenPainting(PaintingEventArgs e) {
+		auto par = cast(Scrollable)_parent;
+		e.graphics.fillRule = GraphicsFillRule.EvenOdd;
+		e.graphics.rectangle(Rect(0, 0, width, height));
+		e.graphics.rectangle(par.contentVisibleRect);
+		e.graphics.clip();
+		e.graphics.source = par.foreColor;
+		Theme.current.Scrollable_paint(par, e.graphics);
+	}
+public:
+	override bool contains(Point pt) {
+		auto par = cast(Scrollable)_parent;
+		if(par is null)
+			return false;
+		return !par.contentVisibleRect.contains(pt);
+	}
+}
+
+class ScrollableContent : Panel {
+private:
+	Scrollable sc;
+	package this(Scrollable s) {
+		sc = s;
+	}
+protected:
+	public override Size bestSize() {
+		return sc.contentBestSize;
+	}
+	override void whenMoved(EventArgs e) {
+		sc.whenContentMoved(e);
+	}
+	override void whenResized(EventArgs e) {
+		sc.whenContentResized(e);
+	}
+	override void whenMouseEntered(EventArgs e) {
+		sc.whenContentMouseEntered(e);
+	}
+	override void whenMouseLeft(EventArgs e) {
+		sc.whenContentMouseLeft(e);
+	}
+	override void whenMouseDown(MouseEventArgs e) {
+		sc.whenContentMouseDown(e);
+	}
+	override void whenMouseUp(MouseEventArgs e) {
+		sc.whenContentMouseUp(e);
+	}
+	override void whenMouseMoved(MouseEventArgs e) {
+		sc.whenContentMouseMoved(e);
+	}
+	override void whenMouseDragged(MouseEventArgs e) {
+		sc.whenContentMouseDragged(e);
+	}
+	override void whenPainting(PaintingEventArgs e) {
+		sc.whenContentPainting(e);
+	}
+}
+
+/**
+ * Here is a skeleton implementation of a scrollable control:
+ * -----
+ * class YourControl : Scrollable {
+ *   protected override void whenContentPainting(PaintingEventArgs e) {
+ * 	   with(e.graphics) {
+ * 	     drawText("Hello World", 5, 5);
+ * 	   }
+ *   }
+ * }
+ * -----
+ */
+abstract class Scrollable : Container {
+private:
+	Panel _content;
+protected:
+	VisibleScrollBars _scrollBars;
+	ScrollBar _hScrollBar, _vScrollBar;
+	ScrollableClipper _clipper;
+
+	real _sbSize;
+	real _leftControlsWidth, _topControlsHeight;
+	// the area the content could be shown if no scroll bars
+	Rect _availableRect;
+	// the area the content is actually visible, after scroll bars are
+	// taken into account
+	Rect _visibleRect;
+	bool _hVisible, _vVisible;
+	override void whenMouseTurned(MouseTurnedEventArgs e) {
+		_vScrollBar.value = _vScrollBar.value + cast(int)(10*e.scrollAmount);
+	}
+
+	Size contentBestSize() { return Size(0, 0); } ///
+	void whenContentMoved(EventArgs e) { } ///
+	void whenContentResized(EventArgs e) { } ///
+	void whenContentMouseEntered(EventArgs e) { } ///
+	void whenContentMouseLeft(EventArgs e) { } ///
+	void whenContentMouseDown(MouseEventArgs e) { } ///
+	void whenContentMouseUp(MouseEventArgs e) { } ///
+	void whenContentMouseMoved(MouseEventArgs e) { } ///
+	void whenContentMouseDragged(MouseEventArgs e) { } ///
+	void whenContentPainting(PaintingEventArgs e) { } ///
+
+	this() {
+		_elasticX = true;
+		_elasticY = true;
+
+		_scrollBars = VisibleScrollBars.Each;
+		//content = c;
+		_content = new ScrollableContent(this);
+		_content.location = [borderSize.left, borderSize.top];
+		add(_content);
+
+		_clipper = new ScrollableClipper;
+		add(_clipper);
+
+		_vScrollBar = new VScrollBar;
+		_vScrollBar.valueChanged += &whenValueChanged;
+		_hScrollBar = new HScrollBar;
+		_hScrollBar.valueChanged += &whenValueChanged;
+		layout();
+	}
+	void whenValueChanged(EventArgs e) {
+		_content.location = [_visibleRect.x-_hScrollBar.value, _visibleRect.y-_vScrollBar.value];
+	}
+public:
+	override void layout() {
+		_sbSize = Theme.current.ScrollBar_size;
+		_leftControlsWidth = 0;
+		//foreach(c; leftControls)
+		//	total += c.bestSize.width;
+		_topControlsHeight = 0;
+		//foreach(c; topControls)
+		//	total += c.bestSize.height;
+		_availableRect = Rect(0, 0, width, height) - borderSize -
+			BorderSize(_leftControlsWidth, _topControlsHeight, 0, 0);
+		_hVisible = HScrollBarVisible;
+		_vVisible = VScrollBarVisible;
+		_visibleRect = _availableRect;
+		if(_hVisible)
+			_visibleRect.height = _visibleRect.height - _sbSize;
+		if(_vVisible)
+			_visibleRect.width = _visibleRect.width - _sbSize;
+
+		remove(_hScrollBar);
+		remove(_vScrollBar);
+		if(_hVisible)
+			add(_hScrollBar);
+		if(_vVisible)
+			add(_vScrollBar);
+
+		_content.size = _content.bestSize;
+		if(_content.width < _visibleRect.width)
+			_content.size = Size(_visibleRect.width, _content.height);
+		if(_content.height < _visibleRect.height)
+			_content.size = Size(_content.width, _visibleRect.height);
+		_clipper.size = size;
+
+		_vScrollBar.maxValue = cast(int)(_content.height-_visibleRect.height);
+		_vScrollBar.visibleValue = _visibleRect.height/_content.height;
+		_hScrollBar.maxValue = cast(int)(_content.width-_visibleRect.width);
+		_hScrollBar.visibleValue = _visibleRect.width/_content.width;
+
+		_vScrollBar.location = [_visibleRect.right, _visibleRect.y];
+		_vScrollBar.size = [_sbSize, _visibleRect.height];
+		_hScrollBar.location = [_visibleRect.x, _visibleRect.bottom];
+		_hScrollBar.size = [_visibleRect.width, _sbSize];
+
+		whenValueChanged(null);
+	}
+	/**
+	 * Gets or sets which scroll bars are shown. The default is Each.
+	 */
+	VisibleScrollBars visibleScrollBars() { return _scrollBars; }
+	/// ditto
+	void visibleScrollBars(VisibleScrollBars bars) { // TODO: rename? SBPolicy?
+		_scrollBars = bars;
+	}
+	/**
+	 * Gets whether the horizontal scroll bar is currently shown.
+	 */
+	bool HScrollBarVisible() {
+		switch(_scrollBars) {
+		case VisibleScrollBars.None:
+		case VisibleScrollBars.Vertical:
+			return false;
+		case VisibleScrollBars.Horizontal:
+			return _content.bestSize.width > _availableRect.width;
+		case VisibleScrollBars.Each:
+			// if vertical scroll bar shown
+			if(_content.bestSize.height > _availableRect.height)
+				return _content.bestSize.width > _availableRect.width-_sbSize;
+			else
+				return _content.bestSize.width > _availableRect.width;
+		case VisibleScrollBars.Both:
+			return _content.bestSize.width > _availableRect.width ||
+				_content.bestSize.height > _availableRect.height;
+		}
+	}
+	/**
+	 * Gets whether the vertical scroll bar is currently shown.
+	 */
+	bool VScrollBarVisible() {
+		switch(_scrollBars) {
+		case VisibleScrollBars.None:
+		case VisibleScrollBars.Horizontal:
+			return false;
+		case VisibleScrollBars.Vertical:
+			return _content.bestSize.height > _availableRect.height;
+		case VisibleScrollBars.Each:
+			// if horizontal scroll bar shown
+			if(_content.bestSize.width > _availableRect.width)
+				return _content.bestSize.height > _availableRect.height-_sbSize;
+			else
+				return _content.bestSize.height > _availableRect.height;
+		case VisibleScrollBars.Both:
+			return _content.bestSize.height > _availableRect.height ||
+				_content.bestSize.width > _availableRect.width;
+		}
+	}
+	/**
+	 * Gets the combined width of all the controls docked on the left side of
+	 * this scrollable.
+	 */
+	real leftControlsWidth() {
+		return _leftControlsWidth;
+	}
+	/**
+	 * Gets the combined height of all the controls docked on the top side of
+	 * this scrollable.
+	 */
+	real topControlsHeight() {
+		return _topControlsHeight;
+	}
+
+	/// Returns the area inside the border, scroll bars, and any controls on the side.
+	Rect contentVisibleRect() {
+		return _visibleRect;
+	}
+	Panel content() { return _content; }
+	BorderSize borderSize() {
+		return Theme.current.Scrollable_borderSize(this);
+	}
+}
+