comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:aa4efef0f0b1
1 // Written in the D programming language
2 // www.digitalmars.com/d/
3
4 /*
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is the Dynamin library.
16 *
17 * The Initial Developer of the Original Code is Jordan Miner.
18 * Portions created by the Initial Developer are Copyright (C) 2007-2009
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Jordan Miner <jminer7@gmail.com>
23 *
24 */
25
26 module dynamin.gui.scrollable;
27
28 import dynamin.all_core;
29 import dynamin.all_painting;
30 import dynamin.all_gui;
31 import tango.io.Stdout;
32
33 ///
34 enum VisibleScrollBars {
35 /**
36 * Shows a vertical scroll bar when it is needed. A horizontal scroll bar
37 * is never shown. A text box with line wrap turned on could use this
38 * option.
39 */
40 Vertical,
41 /**
42 * Shows a horizontal scroll bar when it is needed. A vertical scroll bar
43 * is never shown. The list view in Windows Explorer could use this option.
44 */
45 Horizontal,
46 /**
47 * Shows either scroll bar when it is needed.
48 * A control, such as the detail view in Windows Explorer, that changes its
49 * width when not being scrolled should use this option.
50 */
51 Each,
52 /**
53 * Shows both scroll bars when both are needed.
54 * A control, such as a text box, that changes its width based upon
55 * where it is scrolled to should use this option.
56 */
57 Both,
58 /**
59 * Does not show any scroll bars, even if they are needed.
60 * A single-line text box uses this option.
61 */
62 None
63 }
64
65 class ScrollableClipper : Control {
66 protected:
67 override void whenPainting(PaintingEventArgs e) {
68 auto par = cast(Scrollable)_parent;
69 e.graphics.fillRule = GraphicsFillRule.EvenOdd;
70 e.graphics.rectangle(Rect(0, 0, width, height));
71 e.graphics.rectangle(par.contentVisibleRect);
72 e.graphics.clip();
73 e.graphics.source = par.foreColor;
74 Theme.current.Scrollable_paint(par, e.graphics);
75 }
76 public:
77 override bool contains(Point pt) {
78 auto par = cast(Scrollable)_parent;
79 if(par is null)
80 return false;
81 return !par.contentVisibleRect.contains(pt);
82 }
83 }
84
85 class ScrollableContent : Panel {
86 private:
87 Scrollable sc;
88 package this(Scrollable s) {
89 sc = s;
90 }
91 protected:
92 public override Size bestSize() {
93 return sc.contentBestSize;
94 }
95 override void whenMoved(EventArgs e) {
96 sc.whenContentMoved(e);
97 }
98 override void whenResized(EventArgs e) {
99 sc.whenContentResized(e);
100 }
101 override void whenMouseEntered(EventArgs e) {
102 sc.whenContentMouseEntered(e);
103 }
104 override void whenMouseLeft(EventArgs e) {
105 sc.whenContentMouseLeft(e);
106 }
107 override void whenMouseDown(MouseEventArgs e) {
108 sc.whenContentMouseDown(e);
109 }
110 override void whenMouseUp(MouseEventArgs e) {
111 sc.whenContentMouseUp(e);
112 }
113 override void whenMouseMoved(MouseEventArgs e) {
114 sc.whenContentMouseMoved(e);
115 }
116 override void whenMouseDragged(MouseEventArgs e) {
117 sc.whenContentMouseDragged(e);
118 }
119 override void whenPainting(PaintingEventArgs e) {
120 sc.whenContentPainting(e);
121 }
122 }
123
124 /**
125 * Here is a skeleton implementation of a scrollable control:
126 * -----
127 * class YourControl : Scrollable {
128 * protected override void whenContentPainting(PaintingEventArgs e) {
129 * with(e.graphics) {
130 * drawText("Hello World", 5, 5);
131 * }
132 * }
133 * }
134 * -----
135 */
136 abstract class Scrollable : Container {
137 private:
138 Panel _content;
139 protected:
140 VisibleScrollBars _scrollBars;
141 ScrollBar _hScrollBar, _vScrollBar;
142 ScrollableClipper _clipper;
143
144 real _sbSize;
145 real _leftControlsWidth, _topControlsHeight;
146 // the area the content could be shown if no scroll bars
147 Rect _availableRect;
148 // the area the content is actually visible, after scroll bars are
149 // taken into account
150 Rect _visibleRect;
151 bool _hVisible, _vVisible;
152 override void whenMouseTurned(MouseTurnedEventArgs e) {
153 _vScrollBar.value = _vScrollBar.value + cast(int)(10*e.scrollAmount);
154 }
155
156 Size contentBestSize() { return Size(0, 0); } ///
157 void whenContentMoved(EventArgs e) { } ///
158 void whenContentResized(EventArgs e) { } ///
159 void whenContentMouseEntered(EventArgs e) { } ///
160 void whenContentMouseLeft(EventArgs e) { } ///
161 void whenContentMouseDown(MouseEventArgs e) { } ///
162 void whenContentMouseUp(MouseEventArgs e) { } ///
163 void whenContentMouseMoved(MouseEventArgs e) { } ///
164 void whenContentMouseDragged(MouseEventArgs e) { } ///
165 void whenContentPainting(PaintingEventArgs e) { } ///
166
167 this() {
168 _elasticX = true;
169 _elasticY = true;
170
171 _scrollBars = VisibleScrollBars.Each;
172 //content = c;
173 _content = new ScrollableContent(this);
174 _content.location = [borderSize.left, borderSize.top];
175 add(_content);
176
177 _clipper = new ScrollableClipper;
178 add(_clipper);
179
180 _vScrollBar = new VScrollBar;
181 _vScrollBar.valueChanged += &whenValueChanged;
182 _hScrollBar = new HScrollBar;
183 _hScrollBar.valueChanged += &whenValueChanged;
184 layout();
185 }
186 void whenValueChanged(EventArgs e) {
187 _content.location = [_visibleRect.x-_hScrollBar.value, _visibleRect.y-_vScrollBar.value];
188 }
189 public:
190 override void layout() {
191 _sbSize = Theme.current.ScrollBar_size;
192 _leftControlsWidth = 0;
193 //foreach(c; leftControls)
194 // total += c.bestSize.width;
195 _topControlsHeight = 0;
196 //foreach(c; topControls)
197 // total += c.bestSize.height;
198 _availableRect = Rect(0, 0, width, height) - borderSize -
199 BorderSize(_leftControlsWidth, _topControlsHeight, 0, 0);
200 _hVisible = HScrollBarVisible;
201 _vVisible = VScrollBarVisible;
202 _visibleRect = _availableRect;
203 if(_hVisible)
204 _visibleRect.height = _visibleRect.height - _sbSize;
205 if(_vVisible)
206 _visibleRect.width = _visibleRect.width - _sbSize;
207
208 remove(_hScrollBar);
209 remove(_vScrollBar);
210 if(_hVisible)
211 add(_hScrollBar);
212 if(_vVisible)
213 add(_vScrollBar);
214
215 _content.size = _content.bestSize;
216 if(_content.width < _visibleRect.width)
217 _content.size = Size(_visibleRect.width, _content.height);
218 if(_content.height < _visibleRect.height)
219 _content.size = Size(_content.width, _visibleRect.height);
220 _clipper.size = size;
221
222 _vScrollBar.maxValue = cast(int)(_content.height-_visibleRect.height);
223 _vScrollBar.visibleValue = _visibleRect.height/_content.height;
224 _hScrollBar.maxValue = cast(int)(_content.width-_visibleRect.width);
225 _hScrollBar.visibleValue = _visibleRect.width/_content.width;
226
227 _vScrollBar.location = [_visibleRect.right, _visibleRect.y];
228 _vScrollBar.size = [_sbSize, _visibleRect.height];
229 _hScrollBar.location = [_visibleRect.x, _visibleRect.bottom];
230 _hScrollBar.size = [_visibleRect.width, _sbSize];
231
232 whenValueChanged(null);
233 }
234 /**
235 * Gets or sets which scroll bars are shown. The default is Each.
236 */
237 VisibleScrollBars visibleScrollBars() { return _scrollBars; }
238 /// ditto
239 void visibleScrollBars(VisibleScrollBars bars) { // TODO: rename? SBPolicy?
240 _scrollBars = bars;
241 }
242 /**
243 * Gets whether the horizontal scroll bar is currently shown.
244 */
245 bool HScrollBarVisible() {
246 switch(_scrollBars) {
247 case VisibleScrollBars.None:
248 case VisibleScrollBars.Vertical:
249 return false;
250 case VisibleScrollBars.Horizontal:
251 return _content.bestSize.width > _availableRect.width;
252 case VisibleScrollBars.Each:
253 // if vertical scroll bar shown
254 if(_content.bestSize.height > _availableRect.height)
255 return _content.bestSize.width > _availableRect.width-_sbSize;
256 else
257 return _content.bestSize.width > _availableRect.width;
258 case VisibleScrollBars.Both:
259 return _content.bestSize.width > _availableRect.width ||
260 _content.bestSize.height > _availableRect.height;
261 }
262 }
263 /**
264 * Gets whether the vertical scroll bar is currently shown.
265 */
266 bool VScrollBarVisible() {
267 switch(_scrollBars) {
268 case VisibleScrollBars.None:
269 case VisibleScrollBars.Horizontal:
270 return false;
271 case VisibleScrollBars.Vertical:
272 return _content.bestSize.height > _availableRect.height;
273 case VisibleScrollBars.Each:
274 // if horizontal scroll bar shown
275 if(_content.bestSize.width > _availableRect.width)
276 return _content.bestSize.height > _availableRect.height-_sbSize;
277 else
278 return _content.bestSize.height > _availableRect.height;
279 case VisibleScrollBars.Both:
280 return _content.bestSize.height > _availableRect.height ||
281 _content.bestSize.width > _availableRect.width;
282 }
283 }
284 /**
285 * Gets the combined width of all the controls docked on the left side of
286 * this scrollable.
287 */
288 real leftControlsWidth() {
289 return _leftControlsWidth;
290 }
291 /**
292 * Gets the combined height of all the controls docked on the top side of
293 * this scrollable.
294 */
295 real topControlsHeight() {
296 return _topControlsHeight;
297 }
298
299 /// Returns the area inside the border, scroll bars, and any controls on the side.
300 Rect contentVisibleRect() {
301 return _visibleRect;
302 }
303 Panel content() { return _content; }
304 BorderSize borderSize() {
305 return Theme.current.Scrollable_borderSize(this);
306 }
307 }
308