Ticket #15414: v1-0002-SlippyMapBBoxChooser-redesign-SourceButton-using-a-r.patch
File v1-0002-SlippyMapBBoxChooser-redesign-SourceButton-using-a-r.patch, 15.6 KB (added by , 7 years ago) |
---|
-
src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
From de6d4e61e53bc79f3b08f3cea0aca215aeef8f6d Mon Sep 17 00:00:00 2001 From: Robert Scott <code@humanleg.org.uk> Date: Sun, 8 Oct 2017 10:28:26 +0100 Subject: [PATCH 2/3] SlippyMapBBoxChooser: redesign SourceButton using a regular drop-down JPopupMenu this behaves more similarly to other ui components in the application and allows for more extensibility in that we're now able to allow the "show downloaded area" feature to be enabled or disabled through a simple JCheckBoxMenuItem --- .../josm/gui/bbox/SlippyMapBBoxChooser.java | 24 ++- .../openstreetmap/josm/gui/bbox/SourceButton.java | 216 +++++++++------------ 2 files changed, 113 insertions(+), 127 deletions(-) diff --git a/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java b/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java index 6635f4f..93d4358 100644
a b import java.util.Map; 21 21 import java.util.Set; 22 22 import java.util.concurrent.CopyOnWriteArrayList; 23 23 24 import javax.swing.ButtonModel; 25 import javax.swing.JToggleButton; 24 26 import javax.swing.JOptionPane; 25 27 import javax.swing.SpringLayout; 28 import javax.swing.event.ChangeListener; 29 import javax.swing.event.ChangeEvent; 26 30 27 31 import org.openstreetmap.gui.jmapviewer.Coordinate; 28 32 import org.openstreetmap.gui.jmapviewer.JMapViewer; … … import org.openstreetmap.josm.tools.Logging; 55 59 /** 56 60 * This panel displays a map and lets the user chose a {@link BBox}. 57 61 */ 58 public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, MainLayerManager.ActiveLayerChangeListener {62 public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, ChangeListener, MainLayerManager.ActiveLayerChangeListener { 59 63 60 64 /** 61 65 * A list of tile sources that can be used for displaying the map. … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 130 134 private final transient OsmTileLoader uncachedLoader; 131 135 132 136 private final SizeButton iSizeButton; 137 private final ButtonModel showDownloadAreaButtonModel = new JToggleButton.ToggleButtonModel(); 133 138 private final SourceButton iSourceButton; 134 139 private transient Bounds bbox; 135 140 … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 172 177 173 178 List<TileSource> tileSources = getAllTileSources(); 174 179 175 iSourceButton = new SourceButton(this, tileSources); 180 this.showDownloadAreaButtonModel.addChangeListener(this); 181 iSourceButton = new SourceButton(this, tileSources, this.showDownloadAreaButtonModel); 176 182 add(iSourceButton); 177 springLayout.putConstraint(SpringLayout.EAST, iSourceButton, 0, SpringLayout.EAST, this);178 springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 30, SpringLayout.NORTH, this);183 springLayout.putConstraint(SpringLayout.EAST, iSourceButton, -2, SpringLayout.EAST, this); 184 springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 2, SpringLayout.NORTH, this); 179 185 180 186 iSizeButton = new SizeButton(this); 181 187 add(iSizeButton); … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 230 236 // and it has defined bounds. Routine is analogous to that in OsmDataLayer's paint routine (but just different 231 237 // enough to make sharing code impractical) 232 238 final OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer(); 233 if (editLayer != null && Config.getPref().getBoolean("draw.data.downloaded_area", true) && !editLayer.data.getDataSources().isEmpty()) {239 if (editLayer != null && this.showDownloadAreaButtonModel.isSelected() && !editLayer.data.getDataSources().isEmpty()) { 234 240 // initialize area with current viewport 235 241 Rectangle b = this.getBounds(); 236 242 // ensure we comfortably cover full area … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 273 279 this.repaint(); 274 280 } 275 281 282 @Override 283 public void stateChanged(ChangeEvent e) { 284 this.repaint(); 285 } 286 276 287 /** 277 288 * Enables the disk tile cache. 278 289 * @param enabled true to enable, false to disable … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 341 352 this.tileController.setTileCache(new MemoryTileCache()); 342 353 this.setTileSource(tileSource); 343 354 PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique? 355 if (this.iSourceButton.getCurrentSource() != tileSource) { // prevent infinite recursion 356 this.iSourceButton.setCurrentMap(tileSource); 357 } 344 358 } 345 359 346 360 @Override -
src/org/openstreetmap/josm/gui/bbox/SourceButton.java
diff --git a/src/org/openstreetmap/josm/gui/bbox/SourceButton.java b/src/org/openstreetmap/josm/gui/bbox/SourceButton.java index 6c995a1..8b44a39 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.bbox; 3 3 4 import java.awt.Color; 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.Dimension; 6 import java.awt.Font; 7 import java.awt.FontMetrics; 8 import java.awt.Graphics; 9 import java.awt.Graphics2D; 10 import java.awt.Point; 11 import java.awt.RenderingHints; 12 import java.awt.event.MouseAdapter; 13 import java.awt.event.MouseEvent; 14 import java.awt.event.MouseListener; 7 import java.awt.event.ActionListener; 8 import java.awt.event.ActionEvent; 9 import java.util.ArrayList; 15 10 import java.util.Collection; 11 import java.util.Collections; 12 import java.util.Enumeration; 13 import java.util.List; 16 14 15 import javax.swing.AbstractButton; 16 import javax.swing.ButtonGroup; 17 import javax.swing.ButtonModel; 17 18 import javax.swing.ImageIcon; 18 19 import javax.swing.JComponent; 20 import javax.swing.JCheckBoxMenuItem; 21 import javax.swing.JPopupMenu; 22 import javax.swing.JRadioButtonMenuItem; 23 import javax.swing.JToggleButton; 19 24 20 25 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 26 import org.openstreetmap.josm.gui.widgets.PopupMenuButton; 21 27 import org.openstreetmap.josm.tools.CheckParameterUtil; 22 28 import org.openstreetmap.josm.tools.ImageProvider; 23 29 … … import org.openstreetmap.josm.tools.ImageProvider; 25 31 * Button that allows to choose the imagery source used for slippy map background. 26 32 * @since 1390 27 33 */ 28 public class SourceButton extends JComponent { 29 30 private static final int LAYER_HEIGHT = 20; 31 private static final int LEFT_PADDING = 5; 32 private static final int TOP_PADDING = 5; 33 private static final int BOTTOM_PADDING = 5; 34 35 private transient TileSource[] sources; 36 37 private final ImageIcon enlargeImage; 38 private final ImageIcon shrinkImage; 39 private final Dimension hiddenDimension; 40 41 // Calculated after component is added to container 42 private int barWidth; 43 private Dimension shownDimension; 44 private Font font; 34 public class SourceButton extends PopupMenuButton { 35 protected class TileSourceButtonModel extends JToggleButton.ToggleButtonModel implements ActionListener { 36 protected final TileSource tileSource; 37 38 public TileSourceButtonModel(TileSource tileSource_) { 39 super(); 40 this.tileSource = tileSource_; 41 this.addActionListener(this); 42 } 45 43 46 private boolean isEnlarged; 44 @Override 45 public void actionPerformed(ActionEvent e) { 46 if (SourceButton.this.slippyMapBBoxChooser.getTileController().getTileSource() != this.tileSource) { // prevent infinite recursion 47 SourceButton.this.slippyMapBBoxChooser.toggleMapSource(this.tileSource); 48 } 49 } 50 } 47 51 48 private int currentMap; 49 private final SlippyMapBBoxChooser slippyMapBBoxChooser; 52 protected final SlippyMapBBoxChooser slippyMapBBoxChooser; 53 protected final ButtonModel showDownloadAreaButtonModel; 54 private List<TileSource> sources; 55 private ButtonGroup sourceButtonGroup; 50 56 51 57 /** 52 58 * Constructs a new {@code SourceButton}. 53 59 * @param slippyMapBBoxChooser parent slippy map 54 60 * @param sources list of imagery sources to display 55 61 */ 56 public SourceButton(SlippyMapBBoxChooser slippyMapBBoxChooser, Collection<TileSource> sources) { 57 this.slippyMapBBoxChooser = slippyMapBBoxChooser; 58 setSources(sources); 59 enlargeImage = ImageProvider.get("layer-switcher-maximize"); 60 shrinkImage = ImageProvider.get("layer-switcher-minimize"); 62 public SourceButton( 63 SlippyMapBBoxChooser slippyMapBBoxChooser_, 64 Collection<TileSource> sources_, 65 ButtonModel showDownloadAreaButtonModel_ 66 ) { 67 super(new ImageProvider("dialogs/layerlist").getResource().getImageIcon(new Dimension(16, 16))); 68 this.showDownloadAreaButtonModel = showDownloadAreaButtonModel_; 69 this.slippyMapBBoxChooser = slippyMapBBoxChooser_; 70 this.setPreferredSize(new Dimension(24, 24)); 71 this.setSources(sources_); 72 } 73 74 protected void generatePopupMenu() { 75 JPopupMenu pm = new JPopupMenu(); 76 this.sourceButtonGroup = new ButtonGroup(); 77 for (TileSource ts : this.sources) { 78 JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(ts.getName()); 79 TileSourceButtonModel buttonModel = new TileSourceButtonModel(ts); 80 menuItem.setModel(buttonModel); 81 pm.add(menuItem); 82 this.sourceButtonGroup.add(menuItem); 83 84 // attempt to initialize button group matching current state of slippyMapBBoxChooser 85 buttonModel.setSelected(this.slippyMapBBoxChooser.getTileController().getTileSource() == ts); 86 } 87 88 pm.addSeparator(); 61 89 62 hiddenDimension = new Dimension(enlargeImage.getIconWidth(), enlargeImage.getIconHeight()); 63 setPreferredSize(hiddenDimension); 90 JCheckBoxMenuItem showDownloadAreaItem = new JCheckBoxMenuItem(tr("Show downloaded area")); 91 showDownloadAreaItem.setModel(this.showDownloadAreaButtonModel); 92 pm.add(showDownloadAreaItem); 64 93 65 addMouseListener(mouseListener);94 this.setPopupMenu(pm); 66 95 } 67 96 68 private final transient MouseListener mouseListener = new MouseAdapter() { 69 @Override 70 public void mouseReleased(MouseEvent e) { 71 if (e.getButton() == MouseEvent.BUTTON1) { 72 Point point = e.getPoint(); 73 if (isEnlarged) { 74 if (barWidth < point.x && point.y < shrinkImage.getIconHeight()) { 75 toggle(); 76 } else { 77 int result = (point.y - 5) / LAYER_HEIGHT; 78 if (result >= 0 && result < SourceButton.this.sources.length) { 79 SourceButton.this.slippyMapBBoxChooser.toggleMapSource(SourceButton.this.sources[result]); 80 currentMap = result; 81 toggle(); 82 } 83 } 84 } else { 85 toggle(); 86 } 87 } 97 private void setSourceDefault() { 98 Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements(); 99 if (elems.hasMoreElements()) { 100 elems.nextElement().setSelected(true); 88 101 } 89 } ;102 } 90 103 91 104 /** 92 105 * Set the tile sources. 93 106 * @param sources The tile sources to display 94 107 * @since 6364 95 108 */ 96 public final void setSources(Collection<TileSource> sources) { 97 CheckParameterUtil.ensureParameterNotNull(sources, "sources"); 98 this.sources = sources.toArray(new TileSource[sources.size()]); 99 shownDimension = null; 109 public final void setSources(Collection<TileSource> sources_) { 110 CheckParameterUtil.ensureParameterNotNull(sources_, "sources_"); 111 this.sources = new ArrayList<TileSource>(sources_); 112 this.generatePopupMenu(); 113 if (this.sourceButtonGroup.getSelection() == null) { 114 this.setSourceDefault(); 115 } 100 116 } 101 117 102 @Override 103 protected void paintComponent(Graphics graphics) { 104 Graphics2D g = (Graphics2D) graphics.create(); 105 try { 106 calculateShownDimension(); 107 g.setFont(font); 108 if (isEnlarged) { 109 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 110 int radioButtonSize = 10; 111 112 g.setColor(new Color(0, 0, 139, 179)); 113 g.fillRoundRect(0, 0, barWidth + shrinkImage.getIconWidth(), 114 sources.length * LAYER_HEIGHT + TOP_PADDING + BOTTOM_PADDING, 10, 10); 115 for (int i = 0; i < sources.length; i++) { 116 g.setColor(Color.WHITE); 117 g.fillOval(LEFT_PADDING, TOP_PADDING + i * LAYER_HEIGHT + 6, radioButtonSize, radioButtonSize); 118 g.drawString(sources[i].getName(), LEFT_PADDING + radioButtonSize + LEFT_PADDING, 119 TOP_PADDING + i * LAYER_HEIGHT + g.getFontMetrics().getHeight()); 120 if (currentMap == i) { 121 g.setColor(Color.BLACK); 122 g.fillOval(LEFT_PADDING + 1, TOP_PADDING + 7 + i * LAYER_HEIGHT, radioButtonSize - 2, radioButtonSize - 2); 123 } 124 } 125 126 g.drawImage(shrinkImage.getImage(), barWidth, 0, null); 127 } else { 128 g.drawImage(enlargeImage.getImage(), 0, 0, null); 129 } 130 } finally { 131 g.dispose(); 132 } 118 public final Collection<TileSource> getSources() { 119 return Collections.unmodifiableCollection(this.sources); 133 120 } 134 121 135 /** 136 * Toggle the visibility of imagery source list. 137 */ 138 public void toggle() { 139 this.isEnlarged = !this.isEnlarged; 140 calculateShownDimension(); 141 setPreferredSize(isEnlarged ? shownDimension : hiddenDimension); 142 revalidate(); 122 public final TileSource getCurrentSource() { 123 TileSourceButtonModel buttonModel = (TileSourceButtonModel) this.sourceButtonGroup.getSelection(); 124 if (buttonModel != null) { 125 return buttonModel.tileSource; 126 } 127 return null; 143 128 } 144 129 145 130 /** … … public class SourceButton extends JComponent { 147 132 * @param tileSource the new imagery source to use 148 133 */ 149 134 public void setCurrentMap(TileSource tileSource) { 150 for (int i = 0; i < sources.length; i++) { 151 if (sources[i].equals(tileSource)) { 152 currentMap = i; 135 Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements(); 136 while (elems.hasMoreElements()) { 137 AbstractButton b = elems.nextElement(); 138 if (((TileSourceButtonModel) b.getModel()).tileSource == tileSource) { 139 b.setSelected(true); 153 140 return; 154 141 } 155 142 } 156 currentMap = 0; 157 } 158 159 private void calculateShownDimension() { 160 if (shownDimension == null) { 161 font = getFont().deriveFont(Font.BOLD).deriveFont(15.0f); 162 int textWidth = 0; 163 FontMetrics fm = getFontMetrics(font); 164 for (TileSource source: sources) { 165 int width = fm.stringWidth(source.getName()); 166 if (width > textWidth) { 167 textWidth = width; 168 } 169 } 170 barWidth = textWidth + 50; 171 shownDimension = new Dimension(barWidth + shrinkImage.getIconWidth(), sources.length * LAYER_HEIGHT + TOP_PADDING + BOTTOM_PADDING); 172 } 143 // failed to find the correct one 144 this.setSourceDefault(); 173 145 } 174 146 }