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

Last change on this file since 13435 was 13277, checked in by stoecker, 6 years ago

see #15734 - fix icon detection

  • 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 e) {
73 mouseReleased(e);
74 }
75
76 @Override
77 public void mouseReleased(MouseEvent e) {
78 ScrollViewport.this.scrollDirection = NO_SCROLL;
79 timer.stop();
80 }
81
82 @Override
83 public void mousePressed(MouseEvent e) {
84 ScrollViewport.this.scrollDirection = direction;
85 scroll();
86 timer.restart();
87 }
88 }
89
90 private final JViewport vp = new JViewport();
91 private JComponent component;
92
93 private final List<JButton> buttons = new ArrayList<>();
94
95 private final Timer timer = new Timer(100, evt -> scroll());
96
97 private int scrollDirection = NO_SCROLL;
98
99 private final int allowedScrollDirections;
100
101 private final ComponentAdapter refreshButtonsOnResize = new ComponentAdapter() {
102 @Override
103 public void componentResized(ComponentEvent e) {
104 showOrHideButtons();
105 }
106 };
107
108 /**
109 * Create a new scroll viewport
110 * @param c The component to display as content.
111 * @param direction The direction to scroll.
112 * Should be one of {@link #VERTICAL_DIRECTION}, {@link #HORIZONTAL_DIRECTION}, {@link #ALL_DIRECTION}
113 */
114 public ScrollViewport(JComponent c, int direction) {
115 this(direction);
116 add(c);
117 }
118
119 /**
120 * Create a new scroll viewport
121 * @param direction The direction to scroll.
122 * Should be one of {@link #VERTICAL_DIRECTION}, {@link #HORIZONTAL_DIRECTION}, {@link #ALL_DIRECTION}
123 */
124 public ScrollViewport(int direction) {
125 super(new BorderLayout());
126 this.allowedScrollDirections = direction;
127
128 // UP
129 if ((direction & UP_DIRECTION) != 0) {
130 addScrollButton(UP_DIRECTION, /* ICON */ "svpUp", BorderLayout.NORTH);
131 }
132
133 // DOWN
134 if ((direction & DOWN_DIRECTION) != 0) {
135 addScrollButton(DOWN_DIRECTION, /* ICON */ "svpDown", BorderLayout.SOUTH);
136 }
137
138 // LEFT
139 if ((direction & LEFT_DIRECTION) != 0) {
140 addScrollButton(LEFT_DIRECTION, /* ICON */ "svpLeft", BorderLayout.WEST);
141 }
142
143 // RIGHT
144 if ((direction & RIGHT_DIRECTION) != 0) {
145 addScrollButton(RIGHT_DIRECTION, /* ICON */ "svpRight", BorderLayout.EAST);
146 }
147
148 add(vp, BorderLayout.CENTER);
149
150 this.addComponentListener(refreshButtonsOnResize);
151
152 showOrHideButtons();
153
154 if ((direction & VERTICAL_DIRECTION) != 0) {
155 addMouseWheelListener(e -> scroll(0, e.getUnitsToScroll() * 5));
156 } else if ((direction & HORIZONTAL_DIRECTION) != 0) {
157 addMouseWheelListener(e -> scroll(e.getUnitsToScroll() * 5, 0));
158 }
159
160 timer.setRepeats(true);
161 timer.setInitialDelay(400);
162 }
163
164 private void addScrollButton(int direction, String icon, String borderLayoutPosition) {
165 JButton button = new JButton();
166 button.addMouseListener(new ScrollViewPortMouseListener(direction));
167 button.setPreferredSize(new Dimension(10, 10));
168 button.setIcon(ImageProvider.get(icon));
169 add(button, borderLayoutPosition);
170 buttons.add(button);
171 }
172
173 /**
174 * Scrolls in the currently selected scroll direction.
175 */
176 public synchronized void scroll() {
177 int direction = scrollDirection;
178
179 if (component == null || direction == NO_SCROLL)
180 return;
181
182 Rectangle viewRect = vp.getViewRect();
183
184 int deltaX = 0;
185 int deltaY = 0;
186
187 if (direction < LEFT_DIRECTION) {
188 deltaY = viewRect.height * 2 / 7;
189 } else {
190 deltaX = viewRect.width * 2 / 7;
191 }
192
193 switch (direction) {
194 case UP_DIRECTION :
195 deltaY *= -1;
196 break;
197 case LEFT_DIRECTION :
198 deltaX *= -1;
199 break;
200 default: // Do nothing
201 }
202
203 scroll(deltaX, deltaY);
204 }
205
206 /**
207 * Scrolls by the given offset
208 * @param deltaX offset x
209 * @param deltaY offset y
210 */
211 public synchronized void scroll(int deltaX, int deltaY) {
212 if (component == null)
213 return;
214 Dimension compSize = component.getSize();
215 Rectangle viewRect = vp.getViewRect();
216
217 int newX = viewRect.x + deltaX;
218 int newY = viewRect.y + deltaY;
219
220 if (newY < 0) {
221 newY = 0;
222 }
223 if (newY > compSize.height - viewRect.height) {
224 newY = compSize.height - viewRect.height;
225 }
226 if (newX < 0) {
227 newX = 0;
228 }
229 if (newX > compSize.width - viewRect.width) {
230 newX = compSize.width - viewRect.width;
231 }
232
233 vp.setViewPosition(new Point(newX, newY));
234 }
235
236 /**
237 * Update the visibility of the buttons
238 * Only show them if the Viewport is too small for the content.
239 */
240 public void showOrHideButtons() {
241 boolean needButtons = false;
242 if ((allowedScrollDirections & VERTICAL_DIRECTION) != 0) {
243 needButtons |= getViewSize().height > getViewRect().height;
244 }
245 if ((allowedScrollDirections & HORIZONTAL_DIRECTION) != 0) {
246 needButtons |= getViewSize().width > getViewRect().width;
247 }
248 for (JButton b : buttons) {
249 b.setVisible(needButtons);
250 }
251 }
252
253 /**
254 * Gets the current visible part of the view
255 * @return The current view rect
256 */
257 public Rectangle getViewRect() {
258 return vp.getViewRect();
259 }
260
261 /**
262 * Gets the size of the view
263 * @return The size
264 */
265 public Dimension getViewSize() {
266 return vp.getViewSize();
267 }
268
269 /**
270 * Gets the position (offset) of the view area
271 * @return The offset
272 */
273 public Point getViewPosition() {
274 return vp.getViewPosition();
275 }
276
277 @Override
278 public Dimension getPreferredSize() {
279 if (component == null) {
280 return vp.getPreferredSize();
281 } else {
282 return component.getPreferredSize();
283 }
284 }
285
286 @Override
287 public Dimension getMinimumSize() {
288 if (component == null) {
289 return vp.getMinimumSize();
290 } else {
291 Dimension minSize = component.getMinimumSize();
292 if ((allowedScrollDirections & HORIZONTAL_DIRECTION) != 0) {
293 minSize = new Dimension(20, minSize.height);
294 }
295 if ((allowedScrollDirections & VERTICAL_DIRECTION) != 0) {
296 minSize = new Dimension(minSize.width, 20);
297 }
298 return minSize;
299 }
300 }
301
302 /**
303 * Sets the component to be used as content.
304 * @param c The component
305 */
306 public void add(JComponent c) {
307 vp.removeAll();
308 if (this.component != null) {
309 this.component.removeComponentListener(refreshButtonsOnResize);
310 }
311 this.component = c;
312 c.addComponentListener(refreshButtonsOnResize);
313 vp.add(c);
314 }
315}
Note: See TracBrowser for help on using the repository browser.