Ticket #15414: v2-0002-SlippyMapBBoxChooser-redesign-SourceButton-using-a-r.patch
File v2-0002-SlippyMapBBoxChooser-redesign-SourceButton-using-a-r.patch, 17.0 KB (added by , 7 years ago) |
---|
-
src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
From 4e0398ffdb88d1cfd4b6118846f48dfe49e251f8 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/2] 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 | 30 ++- .../openstreetmap/josm/gui/bbox/SourceButton.java | 223 +++++++++------------ 2 files changed, 125 insertions(+), 128 deletions(-) diff --git a/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java b/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java index 6635f4f..0bdc219 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.data.imagery.ImageryLayerInfo; 43 47 import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader; 44 48 import org.openstreetmap.josm.data.imagery.TileLoaderFactory; 45 49 import org.openstreetmap.josm.data.osm.BBox; 50 import org.openstreetmap.josm.data.preferences.BooleanProperty; 46 51 import org.openstreetmap.josm.data.preferences.StringProperty; 47 52 import org.openstreetmap.josm.gui.MainApplication; 48 53 import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer; … … import org.openstreetmap.josm.tools.Logging; 55 60 /** 56 61 * This panel displays a map and lets the user chose a {@link BBox}. 57 62 */ 58 public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, MainLayerManager.ActiveLayerChangeListener {63 public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, ChangeListener, MainLayerManager.ActiveLayerChangeListener { 59 64 60 65 /** 61 66 * A list of tile sources that can be used for displaying the map. … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 120 125 } 121 126 122 127 private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik"); 128 private static final BooleanProperty PROP_SHOWDLAREA = new BooleanProperty("slippy_map_chooser.show_downloaded_area", true); 123 129 /** 124 130 * The property name used for the resize button. 125 131 * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 130 136 private final transient OsmTileLoader uncachedLoader; 131 137 132 138 private final SizeButton iSizeButton; 139 private final ButtonModel showDownloadAreaButtonModel; 133 140 private final SourceButton iSourceButton; 134 141 private transient Bounds bbox; 135 142 … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 172 179 173 180 List<TileSource> tileSources = getAllTileSources(); 174 181 175 iSourceButton = new SourceButton(this, tileSources); 182 this.showDownloadAreaButtonModel = new JToggleButton.ToggleButtonModel(); 183 this.showDownloadAreaButtonModel.setSelected(PROP_SHOWDLAREA.get()); 184 this.showDownloadAreaButtonModel.addChangeListener(this); 185 iSourceButton = new SourceButton(this, tileSources, this.showDownloadAreaButtonModel); 176 186 add(iSourceButton); 177 springLayout.putConstraint(SpringLayout.EAST, iSourceButton, 0, SpringLayout.EAST, this);178 springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 30, SpringLayout.NORTH, this);187 springLayout.putConstraint(SpringLayout.EAST, iSourceButton, -2, SpringLayout.EAST, this); 188 springLayout.putConstraint(SpringLayout.NORTH, iSourceButton, 2, SpringLayout.NORTH, this); 179 189 180 190 iSizeButton = new SizeButton(this); 181 191 add(iSizeButton); … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 230 240 // and it has defined bounds. Routine is analogous to that in OsmDataLayer's paint routine (but just different 231 241 // enough to make sharing code impractical) 232 242 final OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer(); 233 if (editLayer != null && Config.getPref().getBoolean("draw.data.downloaded_area", true) && !editLayer.data.getDataSources().isEmpty()) {243 if (editLayer != null && this.showDownloadAreaButtonModel.isSelected() && !editLayer.data.getDataSources().isEmpty()) { 234 244 // initialize area with current viewport 235 245 Rectangle b = this.getBounds(); 236 246 // ensure we comfortably cover full area … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 273 283 this.repaint(); 274 284 } 275 285 286 @Override 287 public void stateChanged(ChangeEvent e) { 288 // fired for the stateChanged event of this.showDownloadAreaButtonModel 289 PROP_SHOWDLAREA.put(this.showDownloadAreaButtonModel.isSelected()); 290 this.repaint(); 291 } 292 276 293 /** 277 294 * Enables the disk tile cache. 278 295 * @param enabled true to enable, false to disable … … public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Mai 341 358 this.tileController.setTileCache(new MemoryTileCache()); 342 359 this.setTileSource(tileSource); 343 360 PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique? 361 if (this.iSourceButton.getCurrentSource() != tileSource) { // prevent infinite recursion 362 this.iSourceButton.setCurrentMap(tileSource); 363 } 344 364 } 345 365 346 366 @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..adb5e77 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; 16 17 import javax.swing.ImageIcon; 18 import javax.swing.JComponent; 11 import java.util.Collections; 12 import java.util.Enumeration; 13 import java.util.List; 14 15 import javax.swing.AbstractButton; 16 import javax.swing.ButtonGroup; 17 import javax.swing.ButtonModel; 18 import javax.swing.JCheckBoxMenuItem; 19 import javax.swing.JPopupMenu; 20 import javax.swing.JRadioButtonMenuItem; 21 import javax.swing.JToggleButton; 19 22 20 23 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 24 import org.openstreetmap.josm.gui.widgets.PopupMenuButton; 21 25 import org.openstreetmap.josm.tools.CheckParameterUtil; 22 26 import org.openstreetmap.josm.tools.ImageProvider; 23 27 … … import org.openstreetmap.josm.tools.ImageProvider; 25 29 * Button that allows to choose the imagery source used for slippy map background. 26 30 * @since 1390 27 31 */ 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; 32 public class SourceButton extends PopupMenuButton { 33 protected class TileSourceButtonModel extends JToggleButton.ToggleButtonModel implements ActionListener { 34 protected final TileSource tileSource; 35 36 public TileSourceButtonModel(TileSource tileSource_) { 37 super(); 38 this.tileSource = tileSource_; 39 this.addActionListener(this); 40 } 45 41 46 private boolean isEnlarged; 42 @Override 43 public void actionPerformed(ActionEvent e) { 44 if (SourceButton.this.slippyMapBBoxChooser.getTileController().getTileSource() != this.tileSource) { // prevent infinite recursion 45 SourceButton.this.slippyMapBBoxChooser.toggleMapSource(this.tileSource); 46 } 47 } 48 } 47 49 48 private int currentMap; 49 private final SlippyMapBBoxChooser slippyMapBBoxChooser; 50 protected final SlippyMapBBoxChooser slippyMapBBoxChooser; 51 protected final ButtonModel showDownloadAreaButtonModel; 52 private List<TileSource> sources; 53 private ButtonGroup sourceButtonGroup; 50 54 51 55 /** 52 56 * Constructs a new {@code SourceButton}. 53 57 * @param slippyMapBBoxChooser parent slippy map 54 58 * @param sources list of imagery sources to display 55 59 */ 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"); 60 public SourceButton( 61 SlippyMapBBoxChooser slippyMapBBoxChooser_, 62 Collection<TileSource> sources_, 63 ButtonModel showDownloadAreaButtonModel_ 64 ) { 65 super(new ImageProvider("dialogs/layerlist").getResource().getImageIcon(new Dimension(16, 16))); 66 this.showDownloadAreaButtonModel = showDownloadAreaButtonModel_; 67 this.slippyMapBBoxChooser = slippyMapBBoxChooser_; 68 this.setPreferredSize(new Dimension(24, 24)); 69 this.setSources(sources_); 70 } 71 72 protected void generatePopupMenu() { 73 JPopupMenu pm = new JPopupMenu(); 74 this.sourceButtonGroup = new ButtonGroup(); 75 for (TileSource ts : this.sources) { 76 JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(ts.getName()); 77 TileSourceButtonModel buttonModel = new TileSourceButtonModel(ts); 78 menuItem.setModel(buttonModel); 79 pm.add(menuItem); 80 this.sourceButtonGroup.add(menuItem); 81 82 // attempt to initialize button group matching current state of slippyMapBBoxChooser 83 buttonModel.setSelected(this.slippyMapBBoxChooser.getTileController().getTileSource() == ts); 84 } 61 85 62 hiddenDimension = new Dimension(enlargeImage.getIconWidth(), enlargeImage.getIconHeight()); 63 setPreferredSize(hiddenDimension); 86 pm.addSeparator(); 64 87 65 addMouseListener(mouseListener); 88 JCheckBoxMenuItem showDownloadAreaItem = new JCheckBoxMenuItem(tr("Show downloaded area")); 89 showDownloadAreaItem.setModel(this.showDownloadAreaButtonModel); 90 pm.add(showDownloadAreaItem); 91 92 this.setPopupMenu(pm); 66 93 } 67 94 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 } 95 private void setSourceDefault() { 96 Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements(); 97 if (elems.hasMoreElements()) { 98 elems.nextElement().setSelected(true); 88 99 } 89 } ;100 } 90 101 91 102 /** 92 103 * Set the tile sources. 93 104 * @param sources The tile sources to display 94 105 * @since 6364 95 106 */ 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; 107 public final void setSources(Collection<TileSource> sources_) { 108 CheckParameterUtil.ensureParameterNotNull(sources_, "sources_"); 109 this.sources = new ArrayList<TileSource>(sources_); 110 this.generatePopupMenu(); 111 if (this.sourceButtonGroup.getSelection() == null) { 112 this.setSourceDefault(); 113 } 100 114 } 101 115 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 } 116 /** 117 * Get the tile sources. 118 * @return unmodifiable collection of tile sources 119 */ 120 public final Collection<TileSource> getSources() { 121 return Collections.unmodifiableCollection(this.sources); 133 122 } 134 123 135 124 /** 136 * Toggle the visibility of imagery source list.125 * Get the currently-selected tile source. 137 126 */ 138 public void toggle() { 139 this.isEnlarged = !this.isEnlarged; 140 calculateShownDimension(); 141 setPreferredSize(isEnlarged ? shownDimension : hiddenDimension); 142 revalidate(); 127 public final TileSource getCurrentSource() { 128 TileSourceButtonModel buttonModel = (TileSourceButtonModel) this.sourceButtonGroup.getSelection(); 129 if (buttonModel != null) { 130 return buttonModel.tileSource; 131 } 132 return null; 143 133 } 144 134 145 135 /** … … public class SourceButton extends JComponent { 147 137 * @param tileSource the new imagery source to use 148 138 */ 149 139 public void setCurrentMap(TileSource tileSource) { 150 for (int i = 0; i < sources.length; i++) { 151 if (sources[i].equals(tileSource)) { 152 currentMap = i; 140 Enumeration<AbstractButton> elems = this.sourceButtonGroup.getElements(); 141 while (elems.hasMoreElements()) { 142 AbstractButton b = elems.nextElement(); 143 if (((TileSourceButtonModel) b.getModel()).tileSource == tileSource) { 144 b.setSelected(true); 153 145 return; 154 146 } 155 147 } 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 } 148 // failed to find the correct one 149 this.setSourceDefault(); 173 150 } 174 151 }