Merge branch 'subtree'
[fanfix.git] / src / jexer / TVScroller.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 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 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 putCharXY(0, 0, GraphicsChars.CP437[0x1E], arrowColor);
216 putCharXY(0, getHeight() - 1, GraphicsChars.CP437[0x1F], arrowColor);
217
218 // Place the box
219 if (bottomValue > topValue) {
220 vLineXY(0, 1, getHeight() - 2, GraphicsChars.CP437[0xB1], barColor);
221 putCharXY(0, boxPosition(), GraphicsChars.BOX, arrowColor);
222 } else {
223 vLineXY(0, 1, getHeight() - 2, GraphicsChars.HATCH, barColor);
224 }
225 }
226
227 // ------------------------------------------------------------------------
228 // TVScroller -------------------------------------------------------------
229 // ------------------------------------------------------------------------
230
231 /**
232 * Get the value that corresponds to being on the top edge of the scroll
233 * bar.
234 *
235 * @return the scroll value
236 */
237 public int getTopValue() {
238 return topValue;
239 }
240
241 /**
242 * Set the value that corresponds to being on the top edge of the scroll
243 * bar.
244 *
245 * @param topValue the new scroll value
246 */
247 public void setTopValue(final int topValue) {
248 this.topValue = topValue;
249 }
250
251 /**
252 * Get the value that corresponds to being on the bottom edge of the
253 * scroll bar.
254 *
255 * @return the scroll value
256 */
257 public int getBottomValue() {
258 return bottomValue;
259 }
260
261 /**
262 * Set the value that corresponds to being on the bottom edge of the
263 * scroll bar.
264 *
265 * @param bottomValue the new scroll value
266 */
267 public void setBottomValue(final int bottomValue) {
268 this.bottomValue = bottomValue;
269 }
270
271 /**
272 * Get current value of the scroll.
273 *
274 * @return the scroll value
275 */
276 public int getValue() {
277 return value;
278 }
279
280 /**
281 * Set current value of the scroll.
282 *
283 * @param value the new scroll value
284 */
285 public void setValue(final int value) {
286 this.value = value;
287 }
288
289 /**
290 * Get the increment for clicking on an arrow.
291 *
292 * @return the increment value
293 */
294 public int getSmallChange() {
295 return smallChange;
296 }
297
298 /**
299 * Set the increment for clicking on an arrow.
300 *
301 * @param smallChange the new increment value
302 */
303 public void setSmallChange(final int smallChange) {
304 this.smallChange = smallChange;
305 }
306
307 /**
308 * Set the increment for clicking in the bar between the box and an
309 * arrow.
310 *
311 * @return the increment value
312 */
313 public int getBigChange() {
314 return bigChange;
315 }
316
317 /**
318 * Set the increment for clicking in the bar between the box and an
319 * arrow.
320 *
321 * @param bigChange the new increment value
322 */
323 public void setBigChange(final int bigChange) {
324 this.bigChange = bigChange;
325 }
326
327 /**
328 * Compute the position of the scroll box (a.k.a. grip, thumb).
329 *
330 * @return Y position of the box, between 1 and height - 2
331 */
332 private int boxPosition() {
333 return (getHeight() - 3) * (value - topValue) / (bottomValue - topValue) + 1;
334 }
335
336 /**
337 * Perform a small step change up.
338 */
339 public void decrement() {
340 if (bottomValue == topValue) {
341 return;
342 }
343 value -= smallChange;
344 if (value < topValue) {
345 value = topValue;
346 }
347 }
348
349 /**
350 * Perform a small step change down.
351 */
352 public void increment() {
353 if (bottomValue == topValue) {
354 return;
355 }
356 value += smallChange;
357 if (value > bottomValue) {
358 value = bottomValue;
359 }
360 }
361
362 /**
363 * Perform a big step change up.
364 */
365 public void bigDecrement() {
366 if (bottomValue == topValue) {
367 return;
368 }
369 value -= bigChange;
370 if (value < topValue) {
371 value = topValue;
372 }
373 }
374
375 /**
376 * Perform a big step change down.
377 */
378 public void bigIncrement() {
379 if (bottomValue == topValue) {
380 return;
381 }
382 value += bigChange;
383 if (value > bottomValue) {
384 value = bottomValue;
385 }
386 }
387
388 /**
389 * Go to the top edge of the scroller.
390 */
391 public void toTop() {
392 value = topValue;
393 }
394
395 /**
396 * Go to the bottom edge of the scroller.
397 */
398 public void toBottom() {
399 value = bottomValue;
400 }
401
402 }