Common Scrollable interface
[fanfix.git] / src / jexer / TVScroller.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer;
30
31 import jexer.bits.CellAttributes;
32 import jexer.bits.GraphicsChars;
33 import jexer.event.TMouseEvent;
34
35 /**
36 * TVScroller implements a simple vertical scroll bar.
37 */
38 public final class TVScroller extends TWidget {
39
40 /**
41 * Value that corresponds to being on the top edge of the scroll bar.
42 */
43 private int topValue = 0;
44
45 /**
46 * Get the value that corresponds to being on the top edge of the scroll
47 * bar.
48 *
49 * @return the scroll value
50 */
51 public int getTopValue() {
52 return topValue;
53 }
54
55 /**
56 * Set the value that corresponds to being on the top edge of the scroll
57 * bar.
58 *
59 * @param topValue the new scroll value
60 */
61 public void setTopValue(final int topValue) {
62 this.topValue = topValue;
63 }
64
65 /**
66 * Value that corresponds to being on the bottom edge of the scroll bar.
67 */
68 private int bottomValue = 100;
69
70 /**
71 * Get the value that corresponds to being on the bottom edge of the
72 * scroll bar.
73 *
74 * @return the scroll value
75 */
76 public int getBottomValue() {
77 return bottomValue;
78 }
79
80 /**
81 * Set the value that corresponds to being on the bottom edge of the
82 * scroll bar.
83 *
84 * @param bottomValue the new scroll value
85 */
86 public void setBottomValue(final int bottomValue) {
87 this.bottomValue = bottomValue;
88 }
89
90 /**
91 * Current value of the scroll.
92 */
93 private int value = 0;
94
95 /**
96 * Get current value of the scroll.
97 *
98 * @return the scroll value
99 */
100 public int getValue() {
101 return value;
102 }
103
104 /**
105 * Set current value of the scroll.
106 *
107 * @param value the new scroll value
108 */
109 public void setValue(final int value) {
110 this.value = value;
111 }
112
113 /**
114 * The increment for clicking on an arrow.
115 */
116 private int smallChange = 1;
117
118 /**
119 * Get the increment for clicking on an arrow.
120 *
121 * @return the increment value
122 */
123 public int getSmallChange() {
124 return smallChange;
125 }
126
127 /**
128 * Set the increment for clicking on an arrow.
129 *
130 * @param smallChange the new increment value
131 */
132 public void setSmallChange(final int smallChange) {
133 this.smallChange = smallChange;
134 }
135
136 /**
137 * The increment for clicking in the bar between the box and an arrow.
138 */
139 private int bigChange = 20;
140
141 /**
142 * Set the increment for clicking in the bar between the box and an
143 * arrow.
144 *
145 * @return the increment value
146 */
147 public int getBigChange() {
148 return bigChange;
149 }
150
151 /**
152 * Set the increment for clicking in the bar between the box and an
153 * arrow.
154 *
155 * @param bigChange the new increment value
156 */
157 public void setBigChange(final int bigChange) {
158 this.bigChange = bigChange;
159 }
160
161 /**
162 * When true, the user is dragging the scroll box.
163 */
164 private boolean inScroll = false;
165
166 /**
167 * Public constructor.
168 *
169 * @param parent parent widget
170 * @param x column relative to parent
171 * @param y row relative to parent
172 * @param height height of scroll bar
173 */
174 public TVScroller(final TWidget parent, final int x, final int y,
175 final int height) {
176
177 // Set parent and window
178 super(parent, x, y, 1, height);
179 }
180
181 /**
182 * Compute the position of the scroll box (a.k.a. grip, thumb).
183 *
184 * @return Y position of the box, between 1 and height - 2
185 */
186 private int boxPosition() {
187 return (getHeight() - 3) * (value - topValue) / (bottomValue - topValue) + 1;
188 }
189
190 /**
191 * Draw a vertical scroll bar.
192 */
193 @Override
194 public void draw() {
195 CellAttributes arrowColor = getTheme().getColor("tscroller.arrows");
196 CellAttributes barColor = getTheme().getColor("tscroller.bar");
197 getScreen().putCharXY(0, 0, GraphicsChars.CP437[0x1E], arrowColor);
198 getScreen().putCharXY(0, getHeight() - 1, GraphicsChars.CP437[0x1F],
199 arrowColor);
200
201 // Place the box
202 if (bottomValue > topValue) {
203 getScreen().vLineXY(0, 1, getHeight() - 2,
204 GraphicsChars.CP437[0xB1], barColor);
205 getScreen().putCharXY(0, boxPosition(), GraphicsChars.BOX,
206 arrowColor);
207 } else {
208 getScreen().vLineXY(0, 1, getHeight() - 2, GraphicsChars.HATCH,
209 barColor);
210 }
211
212 }
213
214 /**
215 * Perform a small step change up.
216 */
217 public void decrement() {
218 if (bottomValue == topValue) {
219 return;
220 }
221 value -= smallChange;
222 if (value < topValue) {
223 value = topValue;
224 }
225 }
226
227 /**
228 * Perform a small step change down.
229 */
230 public void increment() {
231 if (bottomValue == topValue) {
232 return;
233 }
234 value += smallChange;
235 if (value > bottomValue) {
236 value = bottomValue;
237 }
238 }
239
240 /**
241 * Perform a big step change up.
242 */
243 public void bigDecrement() {
244 if (bottomValue == topValue) {
245 return;
246 }
247 value -= bigChange;
248 if (value < topValue) {
249 value = topValue;
250 }
251 }
252
253 /**
254 * Perform a big step change down.
255 */
256 public void bigIncrement() {
257 if (bottomValue == topValue) {
258 return;
259 }
260 value += bigChange;
261 if (value > bottomValue) {
262 value = bottomValue;
263 }
264 }
265
266 /**
267 * Go to the top edge of the scroller.
268 */
269 public void toTop() {
270 value = topValue;
271 }
272
273 /**
274 * Go to the bottom edge of the scroller.
275 */
276 public void toBottom() {
277 value = bottomValue;
278 }
279
280 /**
281 * Handle mouse button releases.
282 *
283 * @param mouse mouse button release event
284 */
285 @Override
286 public void onMouseUp(final TMouseEvent mouse) {
287 if (bottomValue == topValue) {
288 return;
289 }
290
291 if (inScroll) {
292 inScroll = false;
293 return;
294 }
295
296 if ((mouse.getX() == 0)
297 && (mouse.getY() == 0)
298 ) {
299 // Clicked on the top arrow
300 decrement();
301 return;
302 }
303
304 if ((mouse.getX() == 0)
305 && (mouse.getY() == getHeight() - 1)
306 ) {
307 // Clicked on the bottom arrow
308 increment();
309 return;
310 }
311
312 if ((mouse.getX() == 0)
313 && (mouse.getY() > 0)
314 && (mouse.getY() < boxPosition())
315 ) {
316 // Clicked between the top arrow and the box
317 value -= bigChange;
318 if (value < topValue) {
319 value = topValue;
320 }
321 return;
322 }
323
324 if ((mouse.getX() == 0)
325 && (mouse.getY() > boxPosition())
326 && (mouse.getY() < getHeight() - 1)
327 ) {
328 // Clicked between the box and the bottom arrow
329 value += bigChange;
330 if (value > bottomValue) {
331 value = bottomValue;
332 }
333 return;
334 }
335 }
336
337 /**
338 * Handle mouse movement events.
339 *
340 * @param mouse mouse motion event
341 */
342 @Override
343 public void onMouseMotion(final TMouseEvent mouse) {
344 if (bottomValue == topValue) {
345 return;
346 }
347
348 if ((mouse.isMouse1())
349 && (inScroll)
350 && (mouse.getY() > 0)
351 && (mouse.getY() < getHeight() - 1)
352 ) {
353 // Recompute value based on new box position
354 value = (bottomValue - topValue)
355 * (mouse.getY()) / (getHeight() - 3) + topValue;
356 return;
357 }
358
359 inScroll = false;
360 }
361
362 /**
363 * Handle mouse press events.
364 *
365 * @param mouse mouse button press event
366 */
367 @Override
368 public void onMouseDown(final TMouseEvent mouse) {
369 if (bottomValue == topValue) {
370 return;
371 }
372
373 if ((mouse.getX() == 0)
374 && (mouse.getY() == boxPosition())
375 ) {
376 inScroll = true;
377 return;
378 }
379 }
380
381 }