Merge branch 'master' of https://github.com/klamonte/jexer
[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 // Variables --------------------------------------------------------------
42 // ------------------------------------------------------------------------
43
44 /**
45 * Value that corresponds to being on the top edge of the scroll bar.
46 */
47 private int topValue = 0;
48
49 /**
50 * Value that corresponds to being on the bottom edge of the scroll bar.
51 */
52 private int bottomValue = 100;
53
54 /**
55 * Current value of the scroll.
56 */
57 private int value = 0;
58
59 /**
60 * The increment for clicking on an arrow.
61 */
62 private int smallChange = 1;
63
64 /**
65 * The increment for clicking in the bar between the box and an arrow.
66 */
67 private int bigChange = 20;
68
69 /**
70 * When true, the user is dragging the scroll box.
71 */
72 private boolean inScroll = false;
73
74 // ------------------------------------------------------------------------
75 // Constructors -----------------------------------------------------------
76 // ------------------------------------------------------------------------
77
78 /**
79 * Public constructor.
80 *
81 * @param parent parent widget
82 * @param x column relative to parent
83 * @param y row relative to parent
84 * @param height height of scroll bar
85 */
86 public TVScroller(final TWidget parent, final int x, final int y,
87 final int height) {
88
89 // Set parent and window
90 super(parent, x, y, 1, height);
91 }
92
93 // ------------------------------------------------------------------------
94 // Event handlers ---------------------------------------------------------
95 // ------------------------------------------------------------------------
96
97 /**
98 * Handle mouse button releases.
99 *
100 * @param mouse mouse button release event
101 */
102 @Override
103 public void onMouseUp(final TMouseEvent mouse) {
104 if (bottomValue == topValue) {
105 return;
106 }
107
108 if (inScroll) {
109 inScroll = false;
110 return;
111 }
112
113 if ((mouse.getX() == 0)
114 && (mouse.getY() == 0)
115 ) {
116 // Clicked on the top arrow
117 decrement();
118 return;
119 }
120
121 if ((mouse.getX() == 0)
122 && (mouse.getY() == getHeight() - 1)
123 ) {
124 // Clicked on the bottom arrow
125 increment();
126 return;
127 }
128
129 if ((mouse.getX() == 0)
130 && (mouse.getY() > 0)
131 && (mouse.getY() < boxPosition())
132 ) {
133 // Clicked between the top arrow and the box
134 value -= bigChange;
135 if (value < topValue) {
136 value = topValue;
137 }
138 return;
139 }
140
141 if ((mouse.getX() == 0)
142 && (mouse.getY() > boxPosition())
143 && (mouse.getY() < getHeight() - 1)
144 ) {
145 // Clicked between the box and the bottom arrow
146 value += bigChange;
147 if (value > bottomValue) {
148 value = bottomValue;
149 }
150 return;
151 }
152 }
153
154 /**
155 * Handle mouse movement events.
156 *
157 * @param mouse mouse motion event
158 */
159 @Override
160 public void onMouseMotion(final TMouseEvent mouse) {
161 if (bottomValue == topValue) {
162 return;
163 }
164
165 if ((mouse.isMouse1())
166 && (inScroll)
167 && (mouse.getY() > 0)
168 && (mouse.getY() < getHeight() - 1)
169 ) {
170 // Recompute value based on new box position
171 value = (bottomValue - topValue)
172 * (mouse.getY()) / (getHeight() - 3) + topValue;
173 if (value > bottomValue) {
174 value = bottomValue;
175 }
176 if (value < topValue) {
177 value = topValue;
178 }
179 return;
180 }
181
182 inScroll = false;
183 }
184
185 /**
186 * Handle mouse press events.
187 *
188 * @param mouse mouse button press event
189 */
190 @Override
191 public void onMouseDown(final TMouseEvent mouse) {
192 if (bottomValue == topValue) {
193 return;
194 }
195
196 if ((mouse.getX() == 0)
197 && (mouse.getY() == boxPosition())
198 ) {
199 inScroll = true;
200 return;
201 }
202 }
203
204 // ------------------------------------------------------------------------
205 // TWidget ----------------------------------------------------------------
206 // ------------------------------------------------------------------------
207
208 /**
209 * Draw a vertical scroll bar.
210 */
211 @Override
212 public void draw() {
213 CellAttributes arrowColor = getTheme().getColor("tscroller.arrows");
214 CellAttributes barColor = getTheme().getColor("tscroller.bar");
215 getScreen().putCharXY(0, 0, GraphicsChars.CP437[0x1E], arrowColor);
216 getScreen().putCharXY(0, getHeight() - 1, GraphicsChars.CP437[0x1F],
217 arrowColor);
218
219 // Place the box
220 if (bottomValue > topValue) {
221 getScreen().vLineXY(0, 1, getHeight() - 2,
222 GraphicsChars.CP437[0xB1], barColor);
223 getScreen().putCharXY(0, boxPosition(), GraphicsChars.BOX,
224 arrowColor);
225 } else {
226 getScreen().vLineXY(0, 1, getHeight() - 2, GraphicsChars.HATCH,
227 barColor);
228 }
229
230 }
231
232 // ------------------------------------------------------------------------
233 // TVScroller -------------------------------------------------------------
234 // ------------------------------------------------------------------------
235
236 /**
237 * Get the value that corresponds to being on the top edge of the scroll
238 * bar.
239 *
240 * @return the scroll value
241 */
242 public int getTopValue() {
243 return topValue;
244 }
245
246 /**
247 * Set the value that corresponds to being on the top edge of the scroll
248 * bar.
249 *
250 * @param topValue the new scroll value
251 */
252 public void setTopValue(final int topValue) {
253 this.topValue = topValue;
254 }
255
256 /**
257 * Get the value that corresponds to being on the bottom edge of the
258 * scroll bar.
259 *
260 * @return the scroll value
261 */
262 public int getBottomValue() {
263 return bottomValue;
264 }
265
266 /**
267 * Set the value that corresponds to being on the bottom edge of the
268 * scroll bar.
269 *
270 * @param bottomValue the new scroll value
271 */
272 public void setBottomValue(final int bottomValue) {
273 this.bottomValue = bottomValue;
274 }
275
276 /**
277 * Get current value of the scroll.
278 *
279 * @return the scroll value
280 */
281 public int getValue() {
282 return value;
283 }
284
285 /**
286 * Set current value of the scroll.
287 *
288 * @param value the new scroll value
289 */
290 public void setValue(final int value) {
291 this.value = value;
292 }
293
294 /**
295 * Get the increment for clicking on an arrow.
296 *
297 * @return the increment value
298 */
299 public int getSmallChange() {
300 return smallChange;
301 }
302
303 /**
304 * Set the increment for clicking on an arrow.
305 *
306 * @param smallChange the new increment value
307 */
308 public void setSmallChange(final int smallChange) {
309 this.smallChange = smallChange;
310 }
311
312 /**
313 * Set the increment for clicking in the bar between the box and an
314 * arrow.
315 *
316 * @return the increment value
317 */
318 public int getBigChange() {
319 return bigChange;
320 }
321
322 /**
323 * Set the increment for clicking in the bar between the box and an
324 * arrow.
325 *
326 * @param bigChange the new increment value
327 */
328 public void setBigChange(final int bigChange) {
329 this.bigChange = bigChange;
330 }
331
332 /**
333 * Compute the position of the scroll box (a.k.a. grip, thumb).
334 *
335 * @return Y position of the box, between 1 and height - 2
336 */
337 private int boxPosition() {
338 return (getHeight() - 3) * (value - topValue) / (bottomValue - topValue) + 1;
339 }
340
341 /**
342 * Perform a small step change up.
343 */
344 public void decrement() {
345 if (bottomValue == topValue) {
346 return;
347 }
348 value -= smallChange;
349 if (value < topValue) {
350 value = topValue;
351 }
352 }
353
354 /**
355 * Perform a small step change down.
356 */
357 public void increment() {
358 if (bottomValue == topValue) {
359 return;
360 }
361 value += smallChange;
362 if (value > bottomValue) {
363 value = bottomValue;
364 }
365 }
366
367 /**
368 * Perform a big step change up.
369 */
370 public void bigDecrement() {
371 if (bottomValue == topValue) {
372 return;
373 }
374 value -= bigChange;
375 if (value < topValue) {
376 value = topValue;
377 }
378 }
379
380 /**
381 * Perform a big step change down.
382 */
383 public void bigIncrement() {
384 if (bottomValue == topValue) {
385 return;
386 }
387 value += bigChange;
388 if (value > bottomValue) {
389 value = bottomValue;
390 }
391 }
392
393 /**
394 * Go to the top edge of the scroller.
395 */
396 public void toTop() {
397 value = topValue;
398 }
399
400 /**
401 * Go to the bottom edge of the scroller.
402 */
403 public void toBottom() {
404 value = bottomValue;
405 }
406
407 }