GT-2855 - fix list cell renderering sizing

This commit is contained in:
dev747368 2019-05-08 16:42:30 -04:00
parent 538cbc1226
commit 131228a250
5 changed files with 77 additions and 81 deletions

View File

@ -32,9 +32,13 @@ import docking.widgets.list.GList;
import ghidra.util.exception.AssertException;
class DirectoryList extends GList<File> implements GhidraFileChooserDirectoryModelIf {
private static final int DEFAULT_ICON_SIZE = 16;
private static final int WIDTH_PADDING = 14;
private static final int HEIGHT_PADDING = 5;
private GhidraFileChooser chooser;
private DirectoryListModel model;
private FileListCellRenderer cellRenderer;
private JLabel listEditorLabel;
private JTextField listEditorField;
private JPanel listEditor;
@ -52,7 +56,7 @@ class DirectoryList extends GList<File> implements GhidraFileChooserDirectoryMod
private void build() {
setLayoutOrientation(JList.VERTICAL_WRAP);
setCellRenderer(new FileListCellRenderer(chooser));
setCellRenderer((cellRenderer = new FileListCellRenderer(getFont(), chooser)));
addMouseListener(new MouseAdapter() {
@Override
@ -281,36 +285,6 @@ class DirectoryList extends GList<File> implements GhidraFileChooserDirectoryMod
setSelectedIndices(indices);
}
// overridden to account for the renderers insets, to avoid clipping
@Override
public void setFixedCellWidth(int width) {
int fullWidth = width;
ListCellRenderer<? super File> renderer = getCellRenderer();
if (renderer instanceof JComponent) {
JComponent c = (JComponent) renderer;
Insets insets = c.getInsets();
fullWidth += insets.left + insets.right;
}
super.setFixedCellWidth(fullWidth);
}
// overridden to account for the renderers insets, to avoid clipping
@Override
public void setFixedCellHeight(int height) {
int fullHeight = height;
ListCellRenderer<? super File> renderer = getCellRenderer();
if (renderer instanceof JComponent) {
JComponent c = (JComponent) renderer;
Insets insets = c.getInsets();
fullHeight += insets.top + insets.bottom;
}
super.setFixedCellHeight(fullHeight);
}
private boolean isEditing() {
return (editedFile != null);
}
@ -367,6 +341,37 @@ class DirectoryList extends GList<File> implements GhidraFileChooserDirectoryMod
}
}
/**
* Resizes this list's cell dimensions based on the string widths found in the supplied
* list of files.
* <p>
* If there there are no files, uses the JScrollPane that contains us for the cellwidth.
*
* @param files list of files to use to resize the list's fixed cell dimensions. If null, uses
* the model's current set of files.
*/
void recomputeListCellDimensions(List<File> files) {
files = (files != null) ? files : model.getAllFiles();
Dimension cellDims =
cellRenderer.computePlainTextListCellDimensions(this, files, 0, DEFAULT_ICON_SIZE);
if (cellDims.width == 0 && getParent() != null) {
// special case: if there were no files to measure, use the containing JScrollPane's
// width
if (getParent().getParent() instanceof JScrollPane) {
JScrollPane parent = (JScrollPane) getParent().getParent();
Dimension parentSize = parent.getSize();
Insets insets = parent.getInsets();
cellDims.width =
parentSize.width - (insets != null ? insets.right + insets.left : 0);
}
}
else {
cellDims.width += DEFAULT_ICON_SIZE + WIDTH_PADDING;
}
setFixedCellWidth(cellDims.width);
setFixedCellHeight(cellDims.height + HEIGHT_PADDING);
}
/*junit*/ JTextField getListEditorText() {
return listEditorField;
}

View File

@ -74,6 +74,10 @@ class DirectoryListModel extends AbstractListModel<File> {
return fileList.indexOf(file);
}
public List<File> getAllFiles() {
return new ArrayList<>(fileList);
}
@Override
public int getSize() {
return fileList.size();

View File

@ -16,6 +16,7 @@
package docking.widgets.filechooser;
import java.awt.Component;
import java.awt.Font;
import java.io.File;
import javax.swing.JList;
@ -28,9 +29,11 @@ class FileListCellRenderer extends GListCellRenderer<File> {
private GhidraFileChooser chooser;
private GhidraFileChooserModel model;
public FileListCellRenderer(GhidraFileChooser chooser) {
public FileListCellRenderer(Font font, GhidraFileChooser chooser) {
super(font);
this.chooser = chooser;
this.model = chooser.getModel();
setShouldAlternateRowBackgroundColors(false);
}
@Override

View File

@ -88,9 +88,6 @@ public class GhidraFileChooser extends DialogComponentProvider
static final String NEW_FOLDER = "New Folder";
private static final int PAD = 5;
private static final int DEFAULT_ICON_SIZE = 16;
private static final int HEIGHT_PADDING = 5;
private static final int WIDTH_PADDING = 14;
private static Icon refreshIcon = Icons.REFRESH_ICON;
private static Icon backIcon = ResourceManager.loadImage("images/left.png");
@ -555,18 +552,11 @@ public class GhidraFileChooser extends DialogComponentProvider
private JScrollPane buildDirectoryList() {
directoryListModel = new DirectoryListModel();
// SCR 3392 - 12/7/07
// initially added to resize the cells of the list when the new files are
// inserted into the list (like when creating a single new folder)
directoryListModel.addListDataListener(new ListDataListener() {
@Override
public void contentsChanged(ListDataEvent e) {
int size = directoryListModel.getSize();
List<File> fileList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
fileList.add(directoryListModel.getFile(i));
}
computeListCellDimensions(fileList);
// called when the list changes because a new file is inserted (ie. create new folder action)
directoryList.recomputeListCellDimensions(null);
}
@Override
@ -850,42 +840,6 @@ public class GhidraFileChooser extends DialogComponentProvider
return worker.isBusy();
}
private void computeListCellDimensions(List<File> files) {
Font font = directoryList.getFont();
FontMetrics metrics = directoryList.getFontMetrics(font);
int maxWidth = getDefaultMaxWidth(files, metrics);
directoryList.setFixedCellWidth(maxWidth + DEFAULT_ICON_SIZE + WIDTH_PADDING);
int rowHeight = Math.max(metrics.getHeight(), DEFAULT_ICON_SIZE);
directoryList.setFixedCellHeight(rowHeight + HEIGHT_PADDING);
}
private int getDefaultMaxWidth(List<File> theFiles, FontMetrics metrics) {
int maxWidth = 0;
if (theFiles.size() > 0) {
for (File file : theFiles) {
maxWidth = Math.max(maxWidth, metrics.stringWidth(getDisplayName(file)));
}
return maxWidth;
}
Dimension scrollSize = directoryScroll.getSize();
if (scrollSize.width == 0) {
return 0;
}
Border border = directoryScroll.getBorder();
if (border != null) {
Insets borderInsets = border.getBorderInsets(directoryScroll);
if (borderInsets != null) {
scrollSize.width -= (borderInsets.right + borderInsets.left);
}
}
return scrollSize.width - WIDTH_PADDING - DEFAULT_ICON_SIZE;
}
String getDisplayName(File file) {
if (file != null) {
if (GhidraFileChooser.MY_COMPUTER.equals(getCurrentDirectory())) {
@ -906,8 +860,10 @@ public class GhidraFileChooser extends DialogComponentProvider
}
private void setDirectoryList(File directory, List<File> files) {
// if the visible listing is still the same directory as this incoming list of files
if (currentDirectory().equals(directory)) {
computeListCellDimensions(files);
// recompute list cell dims before causing an update to the model
directoryList.recomputeListCellDimensions(files);
directoryTableModel.setFiles(files);
directoryTable.scrollRectToVisible(new Rectangle(0, 0, 0, 0));
directoryListModel.setFiles(files);

View File

@ -16,6 +16,7 @@
package docking.widgets.list;
import java.awt.*;
import java.util.List;
import java.util.function.Function;
import javax.swing.*;
@ -121,4 +122,31 @@ public class GListCellRenderer<E> extends AbstractGCellRenderer implements ListC
protected void configureFont(JList<? extends E> list, ListModel<? extends E> model, int index) {
setFont(defaultFont);
}
/**
* Returns the width, height necessary to display the largest element in this list.
* <p>
* Useful for setting a JList's fixed cell width and height to the actual necessary size.
* <p>
* NOTE: the items and the renderer must be in plain text mode, not HTML rendering mode.
*
* @param list the JList that uses this cell renderer
* @param items the items to measure
* @param minWidth the minimum width that can be returned
* @param minHeight the minimum height that can be returned
* @return a new Dimension containing a width and height value necessary to display the largest
* element in the list
*/
public Dimension computePlainTextListCellDimensions(JList<? extends E> list,
List<E> items,
int minWidth, int minHeight) {
configureFont(list, list.getModel(), 0);
FontMetrics metrics = getFontMetrics(getFont());
int maxWidth = minWidth;
for (E item : items) {
String text = getItemText(item).toString();
maxWidth = Math.max(maxWidth, metrics.stringWidth(text));
}
return new Dimension(maxWidth, Math.max(metrics.getHeight(), minHeight));
}
}