source: josm/trunk/src/org/openstreetmap/josm/gui/ScrollViewport.java@ 12620

Last change on this file since 12620 was 12391, checked in by michael2402, 7 years ago

See #14794: Documentation for the gui package

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import java.awt.BorderLayout;
5import java.awt.Dimension;
6import java.awt.Point;
7import java.awt.Rectangle;
8import java.awt.event.ComponentAdapter;
9import java.awt.event.ComponentEvent;
10import java.awt.event.MouseAdapter;
11import java.awt.event.MouseEvent;
12import java.util.ArrayList;
13import java.util.List;
14
15import javax.swing.JButton;
16import javax.swing.JComponent;
17import javax.swing.JPanel;
18import javax.swing.JViewport;
19import javax.swing.Timer;
20
21import org.openstreetmap.josm.tools.ImageProvider;
22
23/**
24 * A viewport with UP and DOWN arrow buttons, so that the user can make the
25 * content scroll.
26 *
27 * This should be used for long, vertical toolbars.
28 */
29public class ScrollViewport extends JPanel {
30
31 private static final int NO_SCROLL = 0;
32
33 /**
34 * Direction flag for upwards
35 */
36 public static final int UP_DIRECTION = 1;
37 /**
38 * Direction flag for downwards
39 */
40 public static final int DOWN_DIRECTION = 2;
41 /**
42 * Direction flag for left
43 */
44 public static final int LEFT_DIRECTION = 4;
45 /**
46 * Direction flag for right
47 */
48 public static final int RIGHT_DIRECTION = 8;
49 /**
50 * Allow vertical scrolling
51 */
52 public static final int VERTICAL_DIRECTION = UP_DIRECTION | DOWN_DIRECTION;
53
54 /**
55 * Allow horizontal scrolling
56 */
57 public static final int HORIZONTAL_DIRECTION = LEFT_DIRECTION | RIGHT_DIRECTION;
58
59 /**
60 * Allow scrolling in both directions
61 */
62 public static final int ALL_DIRECTION = HORIZONTAL_DIRECTION | VERTICAL_DIRECTION;
63
64 private class ScrollViewPortMouseListener extends MouseAdapter {
65 private final int direction;
66
67 ScrollViewPortMouseListener(int direction) {
68 this.direction = direction;
69 }
70
71 @Override
72 public void mouseExited(MouseEvent arg0) {
73 ScrollViewport.this.scrollDirection = NO_SCROLL;
74 timer.stop();
75 }
76
77 @Override
78 public void mouseReleased(MouseEvent arg0) {
79 ScrollViewport.this.scrollDirection = NO_SCROLL;
80 timer.stop();
81 }
82
83 @Override public void mousePressed(MouseEvent arg0) {
84 ScrollViewport.this.scrollDirection = direction;
85 scroll();
86 timer.restart();
87 }
88
89 }
90
91 private final JViewport vp = new JViewport();
92 private JComponent component;
93
94 private final List<JButton> buttons = new ArrayList<>();
95
96 private final Timer timer = new Timer(100, evt -> scroll());
97
98 private int scrollDirection = NO_SCROLL;
99
100 private final int allowedScrollDirections;
101
102 private final ComponentAdapter refreshButtonsOnResize = new ComponentAdapter() {
103 @Override
104 public void componentResized(ComponentEvent e) {
105 showOrHideButtons();
106 }
107 };
108
109 /**
110 * Create a new scroll viewport
111 * @param c The component to display as content.
112 * @param direction The direction to scroll.
113 * Should be one of {@link #VERTICAL_DIRECTION}, {@link #HORIZONTAL_DIRECTION}, {@link #ALL_DIRECTION}
114 */
115 public ScrollViewport(JComponent c, int direction) {
116 this(direction);
117 add(c);
118 }
119
120 /**
121 * Create a new scroll viewport
122 * @param direction The direction to scroll.
123 * Should be one of {@link #VERTICAL_DIRECTION}, {@link #HORIZONTAL_DIRECTION}, {@link #ALL_DIRECTION}
124 */
125 public ScrollViewport(int direction) {
126 super(new BorderLayout());
127 this.allowedScrollDirections = direction;
128
129 // UP
130 if ((direction & UP_DIRECTION) != 0) {
131 addScrollButton(UP_DIRECTION, "svpUp", BorderLayout.NORTH);
132 }
133
134 // DOWN
135 if ((direction & DOWN_DIRECTION) != 0) {
136 addScrollButton(DOWN_DIRECTION, "svpDown", BorderLayout.SOUTH);
137 }
138
139 // LEFT
140 if ((direction & LEFT_DIRECTION) != 0) {
141 addScrollButton(LEFT_DIRECTION, "svpLeft", BorderLayout.WEST);
142 }
143
144 // RIGHT
145 if ((direction & RIGHT_DIRECTION) != 0) {
146 addScrollButton(RIGHT_DIRECTION, "svpRight", BorderLayout.EAST);
147 }
148
149 add(vp, BorderLayout.CENTER);
150
151 this.addComponentListener(refreshButtonsOnResize);
152
153 showOrHideButtons();
154
155 if ((direction & VERTICAL_DIRECTION) != 0) {
156 addMouseWheelListener(e -> scroll(0, e.getUnitsToScroll() * 5));
157 } else if ((direction & HORIZONTAL_DIRECTION) != 0) {
158 addMouseWheelListener(e -> scroll(e.getUnitsToScroll() * 5, 0));
159 }
160
161 timer.setRepeats(true);
162 timer.setInitialDelay(400);
163 }
164
165 private void addScrollButton(int direction, String icon, String borderLayoutPosition) {
166 JButton button = new JButton();
167 button.addMouseListener(new ScrollViewPortMouseListener(direction));
168 button.setPreferredSize(new Dimension(10, 10));
169 button.setIcon(ImageProvider.get(icon));
170 add(button, borderLayoutPosition);
171 buttons.add(button);
172 }
173
174 /**
175 * Scrolls in the currently selected scroll direction.
176 */
177 public synchronized void scroll() {
178 int direction = scrollDirection;
179
180 if (component == null || direction == NO_SCROLL)
181 return;
182
183 Rectangle viewRect = vp.getViewRect();
184
185 int deltaX = 0;
186 int deltaY = 0;
187
188 if (direction < LEFT_DIRECTION) {
189 deltaY = viewRect.height * 2 / 7;
190 } else {
191 deltaX = viewRect.width * 2 / 7;
192 }
193
194 switch (direction) {
195 case UP_DIRECTION :
196 deltaY *= -1;
197 break;
198 case LEFT_DIRECTION :
199 deltaX *= -1;
200 break;
201 default: // Do nothing
202 }
203
204 scroll(deltaX, deltaY);
205 }
206
207 /**
208 * Scrolls by the given offset
209 * @param deltaX offset x
210 * @param deltaY offset y
211 */
212 public synchronized void scroll(int deltaX, int deltaY) {
213 if (component == null)
214 return;
215 Dimension compSize = component.getSize();
216 Rectangle viewRect = vp.getViewRect();
217
218 int newX = viewRect.x + deltaX;
219 int newY = viewRect.y + deltaY;
220
221 if (newY < 0) {
222 newY = 0;
223 }
224 if (newY > compSize.height - viewRect.height) {
225 newY = compSize.height - viewRect.height;
226 }
227 if (newX < 0) {
228 newX = 0;
229 }
230 if (newX > compSize.width - viewRect.width) {
231 newX = compSize.width - viewRect.width;
232 }
233
234 vp.setViewPosition(new Point(newX, newY));
235 }
236
237 /**
238 * Update the visibility of the buttons
239 * Only show them if the Viewport is too small for the content.
240 */
241 public void showOrHideButtons() {
242 boolean needButtons = false;
243 if ((allowedScrollDirections & VERTICAL_DIRECTION) != 0) {
244 needButtons |= getViewSize().height > getViewRect().height;
245 }
246 if ((allowedScrollDirections & HORIZONTAL_DIRECTION) != 0) {
247 needButtons |= getViewSize().width > getViewRect().width;
248 }
249 for (JButton b : buttons) {
250 b.setVisible(needButtons);
251 }
252 }
253
254 /**
255 * Gets the current visible part of the view
256 * @return The current view rect
257 */
258 public Rectangle getViewRect() {
259 return vp.getViewRect();
260 }
261
262 /**
263 * Gets the size of the view
264 * @return The size
265 */
266 public Dimension getViewSize() {
267 return vp.getViewSize();
268 }
269
270 /**
271 * Gets the position (offset) of the view area
272 * @return The offset
273 */
274 public Point getViewPosition() {
275 return vp.getViewPosition();
276 }
277
278 @Override
279 public Dimension getPreferredSize() {
280 if (component == null) {
281 return vp.getPreferredSize();
282 } else {
283 return component.getPreferredSize();
284 }
285 }
286
287 @Override
288 public Dimension getMinimumSize() {
289 if (component == null) {
290 return vp.getMinimumSize();
291 } else {
292 Dimension minSize = component.getMinimumSize();
293 if ((allowedScrollDirections & HORIZONTAL_DIRECTION) != 0) {
294 minSize = new Dimension(20, minSize.height);
295 }
296 if ((allowedScrollDirections & VERTICAL_DIRECTION) != 0) {
297 minSize = new Dimension(minSize.width, 20);
298 }
299 return minSize;
300 }
301 }
302
303 /**
304 * Sets the component to be used as content.
305 * @param c The component
306 */
307 public void add(JComponent c) {
308 vp.removeAll();
309 if (this.component != null) {
310 this.component.removeComponentListener(refreshButtonsOnResize);
311 }
312 this.component = c;
313 c.addComponentListener(refreshButtonsOnResize);
314 vp.add(c);
315 }
316}
Note: See TracBrowser for help on using the repository browser.