0
|
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
|