Common Scrollable interface
[fanfix.git] / src / jexer / THScroller.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 * THScroller implements a simple horizontal scroll bar.
37 */
38 public final class THScroller extends TWidget {
39
40 /**
41 * Value that corresponds to being on the left edge of the scroll bar.
42 */
43 private int leftValue = 0;
44
45 /**
46 * Get the value that corresponds to being on the left edge of the scroll
47 * bar.
48 *
49 * @return the scroll value
50 */
51 public int getLeftValue() {
52 return leftValue;
53 }
54
55 /**
56 * Set the value that corresponds to being on the left edge of the
57 * scroll bar.
58 *
59 * @param leftValue the new scroll value
60 */
61 public void setLeftValue(final int leftValue) {
62 this.leftValue = leftValue;
63 }
64
65 /**
66 * Value that corresponds to being on the right edge of the scroll bar.
67 */
68 private int rightValue = 100;
69
70 /**
71 * Get the value that corresponds to being on the right edge of the
72 * scroll bar.
73 *
74 * @return the scroll value
75 */
76 public int getRightValue() {
77 return rightValue;
78 }
79
80 /**
81 * Set the value that corresponds to being on the right edge of the
82 * scroll bar.
83 *
84 * @param rightValue the new scroll value
85 */
86 public void setRightValue(final int rightValue) {
87 this.rightValue = rightValue;
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 width height of scroll bar
173 */
174 public THScroller(final TWidget parent, final int x, final int y,
175 final int width) {
176
177 // Set parent and window
178 super(parent, x, y, width, 1);
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 width - 2
185 */
186 private int boxPosition() {
187 return (getWidth() - 3) * (value - leftValue) / (rightValue - leftValue) + 1;
188 }
189
190 /**
191 * Draw a horizontal 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[0x11], arrowColor);
198 getScreen().putCharXY(getWidth() - 1, 0, GraphicsChars.CP437[0x10],
199 arrowColor);
200
201 // Place the box
202 if (rightValue > leftValue) {
203 getScreen().hLineXY(1, 0, getWidth() - 2, GraphicsChars.CP437[0xB1],
204 barColor);
205 getScreen().putCharXY(boxPosition(), 0, GraphicsChars.BOX,
206 arrowColor);
207 } else {
208 getScreen().hLineXY(1, 0, getWidth() - 2, GraphicsChars.HATCH,
209 barColor);
210 }
211
212 }
213
214 /**
215 * Perform a small step change left.
216 */
217 public void decrement() {
218 if (leftValue == rightValue) {
219 return;
220 }
221 value -= smallChange;
222 if (value < leftValue) {
223 value = leftValue;
224 }
225 }
226
227 /**
228 * Perform a small step change right.
229 */
230 public void increment() {
231 if (leftValue == rightValue) {
232 return;
233 }
234 value += smallChange;
235 if (value > rightValue) {
236 value = rightValue;
237 }
238 }
239
240 /**
241 * Perform a big step change left.
242 */
243 public void bigDecrement() {
244 if (leftValue == rightValue) {
245 return;
246 }
247 value -= bigChange;
248 if (value < leftValue) {
249 value = leftValue;
250 }
251 }
252
253 /**
254 * Perform a big step change right.
255 */
256 public void bigIncrement() {
257 if (rightValue == leftValue) {
258 return;
259 }
260 value += bigChange;
261 if (value > rightValue) {
262 value = rightValue;
263 }
264 }
265
266 /**
267 * Go to the left edge of the scroller.
268 */
269 public void toLeft() {
270 value = leftValue;
271 }
272
273 /**
274 * Go to the right edge of the scroller.
275 */
276 public void toRight() {
277 value = rightValue;
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
288 if (inScroll) {
289 inScroll = false;
290 return;
291 }
292
293 if (rightValue == leftValue) {
294 return;
295 }
296
297 if ((mouse.getX() == 0)
298 && (mouse.getY() == 0)
299 ) {
300 // Clicked on the left arrow
301 decrement();
302 return;
303 }
304
305 if ((mouse.getY() == 0)
306 && (mouse.getX() == getWidth() - 1)
307 ) {
308 // Clicked on the right arrow
309 increment();
310 return;
311 }
312
313 if ((mouse.getY() == 0)
314 && (mouse.getX() > 0)
315 && (mouse.getX() < boxPosition())
316 ) {
317 // Clicked between the left arrow and the box
318 value -= bigChange;
319 if (value < leftValue) {
320 value = leftValue;
321 }
322 return;
323 }
324
325 if ((mouse.getY() == 0)
326 && (mouse.getX() > boxPosition())
327 && (mouse.getX() < getWidth() - 1)
328 ) {
329 // Clicked between the box and the right arrow
330 value += bigChange;
331 if (value > rightValue) {
332 value = rightValue;
333 }
334 return;
335 }
336 }
337
338 /**
339 * Handle mouse movement events.
340 *
341 * @param mouse mouse motion event
342 */
343 @Override
344 public void onMouseMotion(final TMouseEvent mouse) {
345
346 if (rightValue == leftValue) {
347 inScroll = false;
348 return;
349 }
350
351 if ((mouse.isMouse1())
352 && (inScroll)
353 && (mouse.getX() > 0)
354 && (mouse.getX() < getWidth() - 1)
355 ) {
356 // Recompute value based on new box position
357 value = (rightValue - leftValue)
358 * (mouse.getX()) / (getWidth() - 3) + leftValue;
359 return;
360 }
361 inScroll = false;
362 }
363
364 /**
365 * Handle mouse button press events.
366 *
367 * @param mouse mouse button press event
368 */
369 @Override
370 public void onMouseDown(final TMouseEvent mouse) {
371 if (rightValue == leftValue) {
372 inScroll = false;
373 return;
374 }
375
376 if ((mouse.getY() == 0)
377 && (mouse.getX() == boxPosition())
378 ) {
379 inScroll = true;
380 return;
381 }
382
383 }
384
385 }