Added convenience class for tooltip alignment. Fixed an issue where clicking on a JToggleButton would not be recognized. Fixed an issue where JToggleButtons were too wide. Fixed an issue where the arrowIcon wouldn't be displayed in menus. Added ui for fileChooser. Fixed issue where the tooltip border was too small.pull/15/head
@ -0,0 +1,46 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.components; |
||||
|
||||
import javax.swing.*; |
||||
|
||||
public class ShadowButton extends JButton { |
||||
|
||||
public ShadowButton(final Icon icon) { |
||||
super(icon); |
||||
init(); |
||||
} |
||||
|
||||
private void init() { |
||||
setRolloverEnabled(true); |
||||
setOpaque(false); |
||||
putClientProperty("JButton.variant", "shadow"); |
||||
putClientProperty("JButton.square", Boolean.TRUE); |
||||
} |
||||
|
||||
public ShadowButton(final Action action) { |
||||
super(action); |
||||
init(); |
||||
} |
||||
} |
@ -0,0 +1,68 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.components; |
||||
|
||||
import com.weis.darklaf.components.alignment.Alignment; |
||||
import com.weis.darklaf.components.tooltip.ToolTipContext; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
public class TooltipAwareButton extends JButton { |
||||
|
||||
private final ToolTipContext context = new ToolTipContext(this) |
||||
.setAlignment(Alignment.CENTER) |
||||
.setCenterAlignment(Alignment.SOUTH); |
||||
|
||||
public TooltipAwareButton() { |
||||
this(null, null); |
||||
} |
||||
|
||||
public TooltipAwareButton(final String text, final Icon icon) { |
||||
super(text, icon); |
||||
} |
||||
|
||||
public TooltipAwareButton(final Icon icon) { |
||||
this(null, icon); |
||||
} |
||||
|
||||
public TooltipAwareButton(final String text) { |
||||
this(text, null); |
||||
} |
||||
|
||||
public TooltipAwareButton(final Action a) { |
||||
super(a); |
||||
} |
||||
|
||||
@Override |
||||
public Point getToolTipLocation(final MouseEvent event) { |
||||
return context.getToolTipLocation(event); |
||||
} |
||||
|
||||
@Override |
||||
public JToolTip createToolTip() { |
||||
return context.getToolTip(); |
||||
} |
||||
} |
@ -0,0 +1,68 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.components; |
||||
|
||||
import com.weis.darklaf.components.alignment.Alignment; |
||||
import com.weis.darklaf.components.tooltip.ToolTipContext; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
public class TooltipAwareToggleButton extends JToggleButton { |
||||
|
||||
private final ToolTipContext context = new ToolTipContext(this) |
||||
.setAlignment(Alignment.CENTER) |
||||
.setCenterAlignment(Alignment.SOUTH); |
||||
|
||||
public TooltipAwareToggleButton() { |
||||
this(null, null); |
||||
} |
||||
|
||||
public TooltipAwareToggleButton(final String text, final Icon icon) { |
||||
super(text, icon); |
||||
} |
||||
|
||||
public TooltipAwareToggleButton(final Icon icon) { |
||||
this(null, icon); |
||||
} |
||||
|
||||
public TooltipAwareToggleButton(final String text) { |
||||
this(text, null); |
||||
} |
||||
|
||||
public TooltipAwareToggleButton(final Action a) { |
||||
super(a); |
||||
} |
||||
|
||||
@Override |
||||
public Point getToolTipLocation(final MouseEvent event) { |
||||
return context.getToolTipLocation(event); |
||||
} |
||||
|
||||
@Override |
||||
public JToolTip createToolTip() { |
||||
return context.getToolTip(); |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.ui.filechooser; |
||||
|
||||
import com.weis.darklaf.components.border.MutableLineBorder; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
|
||||
public class DarkFileChooserListViewBorder extends MutableLineBorder.UIResource { |
||||
|
||||
public DarkFileChooserListViewBorder() { |
||||
super(1, 1, 1, 1, Color.BLACK); |
||||
} |
||||
|
||||
@Override |
||||
protected Color getColor() { |
||||
return UIManager.getColor("FileChooser.borderColor"); |
||||
} |
||||
} |
@ -0,0 +1,294 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.ui.filechooser; |
||||
|
||||
import com.weis.darklaf.components.TooltipAwareButton; |
||||
import com.weis.darklaf.components.TooltipAwareToggleButton; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import sun.swing.FilePane; |
||||
|
||||
import javax.accessibility.AccessibleContext; |
||||
import javax.swing.*; |
||||
import javax.swing.border.EmptyBorder; |
||||
import javax.swing.filechooser.FileSystemView; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import java.awt.*; |
||||
import java.awt.event.FocusAdapter; |
||||
import java.awt.event.FocusEvent; |
||||
import java.io.File; |
||||
|
||||
public class DarkFileChooserUI extends DarkFileChooserUIBridge { |
||||
|
||||
public DarkFileChooserUI(final JFileChooser b) { |
||||
super(b); |
||||
} |
||||
|
||||
@NotNull |
||||
@Contract("_ -> new") |
||||
public static ComponentUI createUI(final JComponent c) { |
||||
return new DarkFileChooserUI((JFileChooser) c); |
||||
} |
||||
|
||||
@Override |
||||
public void installComponents(final JFileChooser fc) { |
||||
FileSystemView fsv = fc.getFileSystemView(); |
||||
|
||||
fc.setBorder(new EmptyBorder(12, 12, 11, 11)); |
||||
fc.setLayout(new BorderLayout(0, 11)); |
||||
|
||||
filePane = new DarkFilePane(new MetalFileChooserUIAccessor()); |
||||
fc.addPropertyChangeListener(filePane); |
||||
|
||||
// ********************************* //
|
||||
// **** Construct the top panel **** //
|
||||
// ********************************* //
|
||||
|
||||
// Directory manipulation buttons
|
||||
JPanel topPanel = new JPanel(new BorderLayout(11, 0)); |
||||
JPanel topButtonPanel = new JPanel(); |
||||
topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS)); |
||||
topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS); |
||||
|
||||
// Add the top panel to the fileChooser
|
||||
fc.add(topPanel, BorderLayout.NORTH); |
||||
|
||||
// ComboBox Label
|
||||
lookInLabel = new JLabel(lookInLabelText); |
||||
lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic); |
||||
topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS); |
||||
|
||||
// CurrentDir ComboBox
|
||||
directoryComboBox = new JComboBox<>() { |
||||
public Dimension getPreferredSize() { |
||||
Dimension d = super.getPreferredSize(); |
||||
// Must be small enough to not affect total width.
|
||||
d.width = 150; |
||||
return d; |
||||
} |
||||
}; |
||||
directoryComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, |
||||
lookInLabelText); |
||||
lookInLabel.setLabelFor(directoryComboBox); |
||||
directoryComboBoxModel = createDirectoryComboBoxModel(fc); |
||||
directoryComboBox.setModel(directoryComboBoxModel); |
||||
directoryComboBox.addActionListener(directoryComboBoxAction); |
||||
directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); |
||||
directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); |
||||
directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT); |
||||
directoryComboBox.setMaximumRowCount(8); |
||||
|
||||
topPanel.add(directoryComboBox, BorderLayout.CENTER); |
||||
|
||||
// Up Button
|
||||
JButton upFolderButton = new TooltipAwareButton(getChangeToParentDirectoryAction()); |
||||
upFolderButton.setText(null); |
||||
upFolderButton.setIcon(upFolderIcon); |
||||
upFolderButton.setToolTipText(upFolderToolTipText); |
||||
upFolderButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
||||
upFolderAccessibleName); |
||||
upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); |
||||
upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); |
||||
upFolderButton.setMargin(shrinkwrap); |
||||
|
||||
topButtonPanel.add(upFolderButton); |
||||
topButtonPanel.add(Box.createRigidArea(hstrut5)); |
||||
|
||||
// Home Button
|
||||
File homeDir = fsv.getHomeDirectory(); |
||||
String toolTipText = homeFolderToolTipText; |
||||
|
||||
|
||||
JButton b = new TooltipAwareButton(homeFolderIcon); |
||||
b.setToolTipText(toolTipText); |
||||
b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
||||
homeFolderAccessibleName); |
||||
b.setAlignmentX(JComponent.LEFT_ALIGNMENT); |
||||
b.setAlignmentY(JComponent.CENTER_ALIGNMENT); |
||||
b.setMargin(shrinkwrap); |
||||
|
||||
b.addActionListener(getGoHomeAction()); |
||||
topButtonPanel.add(b); |
||||
topButtonPanel.add(Box.createRigidArea(hstrut5)); |
||||
|
||||
// New Directory Button
|
||||
if (!UIManager.getBoolean("FileChooser.readOnly")) { |
||||
b = new TooltipAwareButton(filePane.getNewFolderAction()); |
||||
b.setText(null); |
||||
b.setIcon(newFolderIcon); |
||||
b.setToolTipText(newFolderToolTipText); |
||||
b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
||||
newFolderAccessibleName); |
||||
b.setAlignmentX(JComponent.LEFT_ALIGNMENT); |
||||
b.setAlignmentY(JComponent.CENTER_ALIGNMENT); |
||||
b.setMargin(shrinkwrap); |
||||
} |
||||
topButtonPanel.add(b); |
||||
topButtonPanel.add(Box.createRigidArea(hstrut5)); |
||||
|
||||
// View button group
|
||||
ButtonGroup viewButtonGroup = new ButtonGroup(); |
||||
|
||||
// List Button
|
||||
listViewButton = new TooltipAwareToggleButton(listViewIcon); |
||||
listViewButton.putClientProperty("JButton.square", Boolean.TRUE); |
||||
listViewButton.setToolTipText(listViewButtonToolTipText); |
||||
listViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
||||
listViewButtonAccessibleName); |
||||
|
||||
|
||||
listViewButton.setSelected(true); |
||||
listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); |
||||
listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); |
||||
listViewButton.setMargin(shrinkwrap); |
||||
listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST)); |
||||
topButtonPanel.add(listViewButton); |
||||
topButtonPanel.add(Box.createRigidArea(hstrut5)); |
||||
viewButtonGroup.add(listViewButton); |
||||
|
||||
// Details Button
|
||||
detailsViewButton = new TooltipAwareToggleButton(detailsViewIcon); |
||||
detailsViewButton.putClientProperty("JButton.square", Boolean.TRUE); |
||||
detailsViewButton.setToolTipText(detailsViewButtonToolTipText); |
||||
detailsViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, |
||||
detailsViewButtonAccessibleName); |
||||
detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); |
||||
detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); |
||||
detailsViewButton.setMargin(shrinkwrap); |
||||
detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS)); |
||||
topButtonPanel.add(detailsViewButton); |
||||
viewButtonGroup.add(detailsViewButton); |
||||
|
||||
topButtonPanel.add(Box.createGlue()); |
||||
|
||||
filePane.addPropertyChangeListener(e -> { |
||||
if ("viewType".equals(e.getPropertyName())) { |
||||
int viewType = filePane.getViewType(); |
||||
switch (viewType) { |
||||
case FilePane.VIEWTYPE_LIST: |
||||
listViewButton.setSelected(true); |
||||
break; |
||||
|
||||
case FilePane.VIEWTYPE_DETAILS: |
||||
detailsViewButton.setSelected(true); |
||||
break; |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// ************************************** //
|
||||
// ******* Add the directory pane ******* //
|
||||
// ************************************** //
|
||||
fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS); |
||||
JComponent accessory = fc.getAccessory(); |
||||
if (accessory != null) { |
||||
getAccessoryPanel().add(accessory); |
||||
} |
||||
filePane.setPreferredSize(LIST_PREF_SIZE); |
||||
fc.add(filePane, BorderLayout.CENTER); |
||||
|
||||
// ********************************** //
|
||||
// **** Construct the bottom panel ** //
|
||||
// ********************************** //
|
||||
JPanel bottomPanel = getBottomPanel(); |
||||
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); |
||||
fc.add(bottomPanel, BorderLayout.SOUTH); |
||||
|
||||
// FileName label and textfield
|
||||
JPanel fileNamePanel = new JPanel(); |
||||
fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS)); |
||||
bottomPanel.add(fileNamePanel); |
||||
bottomPanel.add(Box.createRigidArea(vstrut5)); |
||||
|
||||
fileNameLabel = new AlignedLabel(); |
||||
populateFileNameLabel(); |
||||
fileNamePanel.add(fileNameLabel); |
||||
|
||||
@SuppressWarnings("serial") // anonymous class
|
||||
JTextField tmp2 = new JTextField(35) { |
||||
public Dimension getMaximumSize() { |
||||
return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height); |
||||
} |
||||
}; |
||||
fileNameTextField = tmp2; |
||||
fileNamePanel.add(fileNameTextField); |
||||
fileNameLabel.setLabelFor(fileNameTextField); |
||||
fileNameTextField.addFocusListener( |
||||
new FocusAdapter() { |
||||
public void focusGained(final FocusEvent e) { |
||||
if (!getFileChooser().isMultiSelectionEnabled()) { |
||||
filePane.clearSelection(); |
||||
} |
||||
} |
||||
} |
||||
); |
||||
if (fc.isMultiSelectionEnabled()) { |
||||
setFileName(fileNameString(fc.getSelectedFiles())); |
||||
} else { |
||||
setFileName(fileNameString(fc.getSelectedFile())); |
||||
} |
||||
|
||||
|
||||
// Filetype label and combobox
|
||||
JPanel filesOfTypePanel = new JPanel(); |
||||
filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS)); |
||||
bottomPanel.add(filesOfTypePanel); |
||||
|
||||
AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText); |
||||
filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic); |
||||
filesOfTypePanel.add(filesOfTypeLabel); |
||||
|
||||
filterComboBoxModel = createFilterComboBoxModel(); |
||||
fc.addPropertyChangeListener(filterComboBoxModel); |
||||
filterComboBox = new JComboBox<>(filterComboBoxModel); |
||||
if (filterComboBox.getItemCount() == 0) { |
||||
filterComboBox.setEnabled(false); |
||||
} |
||||
filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, |
||||
filesOfTypeLabelText); |
||||
filesOfTypeLabel.setLabelFor(filterComboBox); |
||||
filterComboBox.setRenderer(createFilterComboBoxRenderer()); |
||||
filesOfTypePanel.add(filterComboBox); |
||||
|
||||
// buttons
|
||||
getButtonPanel().setLayout(new ButtonAreaLayout()); |
||||
|
||||
approveButton = new TooltipAwareButton(getApproveButtonText(fc)); |
||||
// Note: Metal does not use mnemonics for approve and cancel
|
||||
approveButton.addActionListener(getApproveSelectionAction()); |
||||
approveButton.setToolTipText(getApproveButtonToolTipText(fc)); |
||||
getButtonPanel().add(approveButton); |
||||
|
||||
cancelButton = new TooltipAwareButton(cancelButtonText); |
||||
cancelButton.setToolTipText(cancelButtonToolTipText); |
||||
cancelButton.addActionListener(getCancelSelectionAction()); |
||||
getButtonPanel().add(cancelButton); |
||||
|
||||
if (fc.getControlButtonsAreShown()) { |
||||
addControlButtons(); |
||||
} |
||||
|
||||
groupLabels(new AlignedLabel[]{fileNameLabel, filesOfTypeLabel}); |
||||
} |
||||
} |
@ -0,0 +1,434 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.ui.filechooser; |
||||
|
||||
import com.weis.darklaf.components.OverlayScrollPane; |
||||
import com.weis.darklaf.ui.table.TextFieldTableCellEditorBorder; |
||||
import com.weis.darklaf.util.DarkUIUtil; |
||||
import sun.awt.AWTAccessor; |
||||
import sun.swing.SwingUtilities2; |
||||
|
||||
import javax.accessibility.AccessibleContext; |
||||
import javax.swing.*; |
||||
import javax.swing.event.ListDataEvent; |
||||
import javax.swing.event.ListDataListener; |
||||
import javax.swing.event.TableModelEvent; |
||||
import javax.swing.table.TableCellRenderer; |
||||
import javax.swing.text.Position; |
||||
import java.awt.*; |
||||
import java.awt.event.ComponentAdapter; |
||||
import java.awt.event.ComponentEvent; |
||||
import java.awt.event.KeyEvent; |
||||
import java.awt.event.MouseEvent; |
||||
import java.io.File; |
||||
import java.util.function.Supplier; |
||||
|
||||
public class DarkFilePane extends DarkFilePaneUIBridge { |
||||
|
||||
|
||||
public DarkFilePane(final FileChooserUIAccessor fileChooserUIAccessor) { |
||||
super(fileChooserUIAccessor); |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults() { |
||||
super.installDefaults(); |
||||
kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes"); |
||||
megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes"); |
||||
gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes"); |
||||
editCell = new JTextField(); |
||||
editCell.setBorder(new TextFieldTableCellEditorBorder()); |
||||
editCell.putClientProperty("JTextField.listCellEditor", true); |
||||
} |
||||
|
||||
public JPanel createList() { |
||||
JPanel p = new JPanel(new BorderLayout()); |
||||
final JFileChooser fileChooser = getFileChooser(); |
||||
|
||||
@SuppressWarnings("serial") // anonymous class
|
||||
final JList<Object> list = new JList<>() { |
||||
public int getNextMatch(final String prefix, final int startIndex, final Position.Bias bias) { |
||||
ListModel<?> model = getModel(); |
||||
int max = model.getSize(); |
||||
if (prefix == null || startIndex < 0 || startIndex >= max) { |
||||
throw new IllegalArgumentException(); |
||||
} |
||||
// start search from the next element before/after the selected element
|
||||
boolean backwards = (bias == Position.Bias.Backward); |
||||
for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ? -1 : 1)) { |
||||
String filename = fileChooser.getName((File) model.getElementAt(i)); |
||||
if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) { |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
}; |
||||
list.setCellRenderer(new FileRenderer()); |
||||
list.setLayoutOrientation(JList.VERTICAL_WRAP); |
||||
|
||||
// 4835633 : tell BasicListUI that this is a file list
|
||||
list.putClientProperty("List.isFileList", Boolean.TRUE); |
||||
list.putClientProperty("JList.fullRowSelection", fullRowSelection); |
||||
|
||||
if (listViewWindowsStyle) { |
||||
list.addFocusListener(repaintListener); |
||||
} |
||||
|
||||
updateListRowCount(list); |
||||
|
||||
getModel().addListDataListener(new ListDataListener() { |
||||
public void intervalAdded(final ListDataEvent e) { |
||||
updateListRowCount(list); |
||||
} |
||||
|
||||
public void intervalRemoved(final ListDataEvent e) { |
||||
updateListRowCount(list); |
||||
} |
||||
|
||||
public void contentsChanged(final ListDataEvent e) { |
||||
if (isShowing()) { |
||||
clearSelection(); |
||||
} |
||||
updateListRowCount(list); |
||||
} |
||||
}); |
||||
|
||||
getModel().addPropertyChangeListener(this); |
||||
|
||||
if (fileChooser.isMultiSelectionEnabled()) { |
||||
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
||||
} else { |
||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
||||
} |
||||
list.setModel(new SortableListModel()); |
||||
|
||||
list.addListSelectionListener(createListSelectionListener()); |
||||
list.addMouseListener(getMouseHandler()); |
||||
|
||||
OverlayScrollPane overlayScrollPane = new OverlayScrollPane(list); |
||||
JScrollPane scrollPane = overlayScrollPane.getScrollPane(); |
||||
if (listViewBackground != null) { |
||||
list.setBackground(listViewBackground); |
||||
} |
||||
if (listViewBorder != null) { |
||||
scrollPane.setBorder(listViewBorder); |
||||
} |
||||
|
||||
list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName); |
||||
|
||||
p.add(overlayScrollPane, BorderLayout.CENTER); |
||||
return p; |
||||
} |
||||
|
||||
@Override |
||||
public JPanel createDetailsView() { |
||||
final JFileChooser chooser = getFileChooser(); |
||||
|
||||
JPanel p = new JPanel(new BorderLayout()); |
||||
|
||||
@SuppressWarnings("serial") // anonymous class
|
||||
final JTable detailsTable = new JTable(getDetailsTableModel()) { |
||||
public void tableChanged(final TableModelEvent e) { |
||||
super.tableChanged(e); |
||||
|
||||
if (e.getFirstRow() == TableModelEvent.HEADER_ROW) { |
||||
// update header with possibly changed column set
|
||||
updateDetailsColumnModel(this); |
||||
} |
||||
} |
||||
|
||||
// Handle Escape key events here
|
||||
protected boolean processKeyBinding(final KeyStroke ks, final KeyEvent e, |
||||
final int condition, final boolean pressed) { |
||||
if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) { |
||||
// We are not editing, forward to filechooser.
|
||||
chooser.dispatchEvent(e); |
||||
return true; |
||||
} |
||||
return super.processKeyBinding(ks, e, condition, pressed); |
||||
} |
||||
}; |
||||
int rowHeight = UIManager.getInt("FileChooser.rowHeight"); |
||||
if (rowHeight > 0) { |
||||
detailsTable.setRowHeight(rowHeight); |
||||
} |
||||
detailsTable.setRowSorter(getRowSorter()); |
||||
detailsTable.setAutoCreateColumnsFromModel(false); |
||||
detailsTable.setComponentOrientation(chooser.getComponentOrientation()); |
||||
detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); |
||||
detailsTable.setRowSelectionAllowed(true); |
||||
detailsTable.setShowGrid(false); |
||||
detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); |
||||
detailsTable.addKeyListener(detailsKeyListener); |
||||
detailsTable.putClientProperty("JTable.rowFocusBorder", true); |
||||
detailsTable.putClientProperty("JTable.fileChooserParent", (Supplier<JFileChooser>) this::getFileChooser); |
||||
detailsTable.putClientProperty("JTable.fileNameColumnIndex", COLUMN_FILENAME); |
||||
|
||||
|
||||
Font font = list.getFont(); |
||||
detailsTable.setFont(font); |
||||
|
||||
TableCellRenderer headerRenderer = |
||||
new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer()); |
||||
detailsTable.getTableHeader().setDefaultRenderer(headerRenderer); |
||||
TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser); |
||||
detailsTable.setDefaultRenderer(Object.class, cellRenderer); |
||||
|
||||
if (getFileChooser().isMultiSelectionEnabled()) { |
||||
detailsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
||||
} else { |
||||
detailsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
||||
} |
||||
|
||||
detailsTable.addMouseListener(getMouseHandler()); |
||||
|
||||
// 4835633 : tell BasicTableUI that this is a file list
|
||||
detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE); |
||||
|
||||
if (listViewWindowsStyle) { |
||||
detailsTable.addFocusListener(repaintListener); |
||||
} |
||||
|
||||
// TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
|
||||
// We don't want them to navigate within the table
|
||||
ActionMap am = SwingUtilities.getUIActionMap(detailsTable); |
||||
am.remove("selectNextRowCell"); |
||||
am.remove("selectPreviousRowCell"); |
||||
am.remove("selectNextColumnCell"); |
||||
am.remove("selectPreviousColumnCell"); |
||||
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, |
||||
null); |
||||
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, |
||||
null); |
||||
|
||||
OverlayScrollPane overlayScrollPane = new OverlayScrollPane(detailsTable); |
||||
JScrollPane scrollPane = overlayScrollPane.getScrollPane(); |
||||
scrollPane.setComponentOrientation(chooser.getComponentOrientation()); |
||||
LookAndFeel.installColors(scrollPane.getViewport(), "Table.background", "Table.foreground"); |
||||
|
||||
// Adjust width of first column so the table fills the viewport when
|
||||
// first displayed (temporary listener).
|
||||
scrollPane.addComponentListener(new ComponentAdapter() { |
||||
public void componentResized(final ComponentEvent e) { |
||||
JScrollPane sp = (JScrollPane) e.getComponent(); |
||||
fixNameColumnWidth(sp.getViewport().getSize().width); |
||||
sp.removeComponentListener(this); |
||||
} |
||||
}); |
||||
|
||||
detailsTable.setForeground(list.getForeground()); |
||||
detailsTable.setBackground(list.getBackground()); |
||||
|
||||
if (listViewBorder != null) { |
||||
scrollPane.setBorder(listViewBorder); |
||||
} |
||||
p.add(overlayScrollPane, BorderLayout.CENTER); |
||||
|
||||
detailsTableModel.fireTableStructureChanged(); |
||||
|
||||
detailsTable.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesDetailsAccessibleName); |
||||
|
||||
return p; |
||||
} |
||||
|
||||
protected void cancelEdit() { |
||||
if (editFile != null) { |
||||
editFile = null; |
||||
list.remove(editCell); |
||||
list.putClientProperty("JList.isEditing", false); |
||||
repaint(); |
||||
} else if (detailsTable != null && detailsTable.isEditing()) { |
||||
detailsTable.getCellEditor().cancelCellEditing(); |
||||
} |
||||
} |
||||
|
||||
protected void editFileName(final int index) { |
||||
JFileChooser chooser = getFileChooser(); |
||||
File currentDirectory = chooser.getCurrentDirectory(); |
||||
|
||||
if (readOnly || !canWrite(currentDirectory, chooser)) { |
||||
return; |
||||
} |
||||
|
||||
ensureIndexIsVisible(index); |
||||
switch (viewType) { |
||||
case VIEWTYPE_LIST: |
||||
editFile = (File) getModel().getElementAt(getRowSorter().convertRowIndexToModel(index)); |
||||
Rectangle r = list.getCellBounds(index, index); |
||||
if (editCell == null) { |
||||
editCell = new JTextField(); |
||||
editCell.setName("Tree.cellEditor"); |
||||
editCell.addActionListener(new EditActionListener()); |
||||
editCell.addFocusListener(editorFocusListener); |
||||
editCell.setNextFocusableComponent(list); |
||||
} |
||||
list.add(editCell); |
||||
editCell.setText(chooser.getName(editFile)); |
||||
ComponentOrientation orientation = list.getComponentOrientation(); |
||||
editCell.setComponentOrientation(orientation); |
||||
|
||||
Icon icon = chooser.getIcon(editFile); |
||||
|
||||
// PENDING - grab padding (4) below from defaults table.
|
||||
int editX = icon == null ? 20 : icon.getIconWidth() + 4; |
||||
|
||||
int gap = 0; |
||||
var renderer = list.getCellRenderer(); |
||||
if (renderer instanceof JLabel) { |
||||
gap = ((JLabel) renderer).getIconTextGap() - 1; |
||||
} |
||||
if (orientation.isLeftToRight()) { |
||||
editCell.setBounds(editX + r.x + gap, r.y, r.width - editX - gap, r.height); |
||||
} else { |
||||
editCell.setBounds(r.x, r.y, r.width - editX - gap, r.height); |
||||
} |
||||
list.putClientProperty("JList.isEditing", true); |
||||
editCell.requestFocus(); |
||||
editCell.selectAll(); |
||||
break; |
||||
|
||||
case VIEWTYPE_DETAILS: |
||||
detailsTable.editCellAt(index, COLUMN_FILENAME); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
public JPopupMenu getComponentPopupMenu() { |
||||
JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu(); |
||||
if (popupMenu != null) { |
||||
return popupMenu; |
||||
} |
||||
|
||||
JMenu viewMenu = getViewMenu(); |
||||
if (contextMenu == null) { |
||||
contextMenu = new JPopupMenu(); |
||||
if (viewMenu != null) { |
||||
contextMenu.add(viewMenu); |
||||
if (listViewWindowsStyle) { |
||||
contextMenu.addSeparator(); |
||||
} |
||||
} |
||||
ActionMap actionMap = getActionMap(); |
||||
Action refreshAction = actionMap.get(ACTION_REFRESH); |
||||
Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER); |
||||
Action renameAction = actionMap.get(ACTION_EDIT_FILE_NAME); |
||||
if (refreshAction != null) { |
||||
contextMenu.add(refreshAction); |
||||
} |
||||
if (renameAction != null) { |
||||
var menuItem = new JMenuItem(renameAction); |
||||
menuItem.setText("Rename"); |
||||
contextMenu.add(menuItem); |
||||
} |
||||
if (newFolderAction != null) { |
||||
contextMenu.add(newFolderAction); |
||||
} |
||||
} |
||||
if (viewMenu != null) { |
||||
viewMenu.getPopupMenu().setInvoker(viewMenu); |
||||
} |
||||
return contextMenu; |
||||
} |
||||
|
||||
@Override |
||||
protected Handler getMouseHandler() { |
||||
if (handler == null) { |
||||
handler = new DarkHandler(); |
||||
} |
||||
return handler; |
||||
} |
||||
|
||||
protected class DarkHandler extends Handler { |
||||
|
||||
@Override |
||||
public void mouseClicked(MouseEvent evt) { |
||||
JComponent source = (JComponent) evt.getSource(); |
||||
|
||||
int index; |
||||
if (source instanceof JList) { |
||||
index = list.locationToIndex(evt.getPoint()); |
||||
} else if (source instanceof JTable) { |
||||
JTable table = (JTable) source; |
||||
Point p = evt.getPoint(); |
||||
index = table.rowAtPoint(p); |
||||
|
||||
boolean pointOutsidePrefSize = SwingUtilities2.pointOutsidePrefSize(table, index, |
||||
table.columnAtPoint(p), p); |
||||
|
||||
if (pointOutsidePrefSize && !fullRowSelection) { |
||||
return; |
||||
} |
||||
|
||||
// Translate point from table to list
|
||||
if (index >= 0 && list != null && listSelectionModel.isSelectedIndex(index)) { |
||||
|
||||
// Make a new event with the list as source, placing the
|
||||
// click in the corresponding list cell.
|
||||
Rectangle r = list.getCellBounds(index, index); |
||||
MouseEvent newEvent = new MouseEvent(list, evt.getID(), |
||||
evt.getWhen(), evt.getModifiersEx(), |
||||
r.x + 1, r.y + r.height / 2, |
||||
evt.getXOnScreen(), |
||||
evt.getYOnScreen(), |
||||
evt.getClickCount(), evt.isPopupTrigger(), |
||||
evt.getButton()); |
||||
AWTAccessor.MouseEventAccessor meAccessor = AWTAccessor.getMouseEventAccessor(); |
||||
meAccessor.setCausedByTouchEvent(newEvent, |
||||
meAccessor.isCausedByTouchEvent(evt)); |
||||
evt = newEvent; |
||||
} |
||||
} else { |
||||
return; |
||||
} |
||||
|
||||
if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) { |
||||
JFileChooser fc = getFileChooser(); |
||||
|
||||
// For single click, we handle editing file name
|
||||
if (evt.getClickCount() == 1 && source instanceof JList) { |
||||
if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1) |
||||
&& listSelectionModel.isSelectedIndex(index) |
||||
&& getEditIndex() == index && editFile == null |
||||
&& DarkUIUtil.isOverText(evt, index, list)) { |
||||
editFileName(index); |
||||
} else { |
||||
setEditIndex(index); |
||||
} |
||||
} else if (evt.getClickCount() == 2) { |
||||
// on double click (open or drill down one directory) be
|
||||
// sure to clear the edit index
|
||||
resetEditIndex(); |
||||
} |
||||
} |
||||
|
||||
// Forward event to Basic
|
||||
if (getDoubleClickListener() != null) { |
||||
list.putClientProperty("List.isFileList", false); |
||||
getDoubleClickListener().mouseClicked(evt); |
||||
list.putClientProperty("List.isFileList", true); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.ui.list; |
||||
|
||||
import com.weis.darklaf.ui.cell.DarkCellBorder; |
||||
import com.weis.darklaf.util.DarkUIUtil; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
|
||||
public class DarkListCellFocusBorder extends DarkCellBorder { |
||||
|
||||
@Override |
||||
public void paintBorder(final Component c, final Graphics g, final int x, final int y, |
||||
final int width, final int height) { |
||||
super.paintBorder(c, g, x, y, width, height); |
||||
g.setColor(UIManager.getColor("List.focusBorderColor")); |
||||
DarkUIUtil.drawRect(g, 0, 0, width, height, 1); |
||||
} |
||||
} |
@ -0,0 +1,44 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.ui.list; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
|
||||
public class DarkListCellRenderer extends DefaultListCellRenderer { |
||||
|
||||
@Override |
||||
public Component getListCellRendererComponent(@NotNull final JList<?> list, final Object value, |
||||
final int index, final boolean isSelected, |
||||
final boolean cellHasFocus) { |
||||
if (Boolean.TRUE.equals(list.getClientProperty("JList.isEditing"))) { |
||||
if (list.getSelectionModel().getLeadSelectionIndex() == index) { |
||||
return super.getListCellRendererComponent(list, value, index, false, false); |
||||
} |
||||
} |
||||
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); |
||||
} |
||||
} |
@ -1,20 +1,119 @@
|
||||
package com.weis.darklaf.ui.list; |
||||
|
||||
import com.weis.darklaf.util.DarkUIUtil; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.basic.BasicListUI; |
||||
import java.awt.*; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
/** |
||||
* @author Jannis Weis |
||||
*/ |
||||
public class DarkListUI extends BasicListUI { |
||||
public class DarkListUI extends DarkListUIBridge { |
||||
|
||||
@NotNull |
||||
@Contract("_ -> new") |
||||
public static ComponentUI createUI(final JComponent list) { |
||||
return new DarkListUI(); |
||||
} |
||||
|
||||
protected void paintCell(final Graphics g, final int row, final Rectangle rowBounds, |
||||
final ListCellRenderer<Object> cellRenderer, final ListModel<Object> dataModel, |
||||
final ListSelectionModel selModel, final int leadIndex) { |
||||
Object value = dataModel.getElementAt(row); |
||||
boolean cellHasFocus = list.hasFocus() && (row == leadIndex); |
||||
boolean isSelected = selModel.isSelectedIndex(row); |
||||
|
||||
Component rendererComponent = |
||||
cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus); |
||||
|
||||
int cx = rowBounds.x; |
||||
int cy = rowBounds.y; |
||||
int cw = rowBounds.width; |
||||
int ch = rowBounds.height; |
||||
|
||||
if (Boolean.TRUE.equals(list.getClientProperty("JList.shrinkWrap"))) { |
||||
// Shrink renderer to preferred size. This is mostly used on Windows
|
||||
// where selection is only shown around the file name, instead of
|
||||
// across the whole list cell.
|
||||
int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4); |
||||
if (!list.getComponentOrientation().isLeftToRight()) { |
||||
cx += (cw - w); |
||||
} |
||||
cw = w; |
||||
} |
||||
|
||||
rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true); |
||||
} |
||||
|
||||
@Override |
||||
protected void installListeners() { |
||||
super.installListeners(); |
||||
list.addMouseListener(new MouseAdapter() { |
||||
|
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
protected Handler getHandler() { |
||||
if (handler == null) { |
||||
handler = new DarkHandler(); |
||||
} |
||||
return handler; |
||||
} |
||||
|
||||
|
||||
protected class DarkHandler extends Handler { |
||||
|
||||
@Override |
||||
protected void adjustSelection(final MouseEvent e) { |
||||
int row = list.locationToIndex(e.getPoint()); |
||||
if (row < 0) { |
||||
// If shift is down in multi-select, we should do nothing.
|
||||
// For single select or non-shift-click, clear the selection
|
||||
if (isFileList && !Boolean.TRUE.equals(list.getClientProperty("JList.fullRowSelection")) |
||||
&& e.getID() == MouseEvent.MOUSE_PRESSED && |
||||
(!e.isShiftDown() || list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) { |
||||
list.clearSelection(); |
||||
} |
||||
} else { |
||||
int anchorIndex = adjustIndex(list.getAnchorSelectionIndex(), list); |
||||
boolean anchorSelected; |
||||
if (anchorIndex == -1) { |
||||
anchorIndex = 0; |
||||
anchorSelected = false; |
||||
} else { |
||||
anchorSelected = list.isSelectedIndex(anchorIndex); |
||||
} |
||||
|
||||
if (DarkUIUtil.isMenuShortcutKeyDown(e)) { |
||||
if (e.isShiftDown()) { |
||||
if (anchorSelected) { |
||||
list.addSelectionInterval(anchorIndex, row); |
||||
} else { |
||||
list.removeSelectionInterval(anchorIndex, row); |
||||
if (isFileList) { |
||||
list.addSelectionInterval(row, row); |
||||
list.getSelectionModel().setAnchorSelectionIndex(anchorIndex); |
||||
} |
||||
} |
||||
} else if (list.isSelectedIndex(row)) { |
||||
list.removeSelectionInterval(row, row); |
||||
} else { |
||||
list.addSelectionInterval(row, row); |
||||
} |
||||
} else if (e.isShiftDown()) { |
||||
list.setSelectionInterval(anchorIndex, row); |
||||
} else { |
||||
list.setSelectionInterval(row, row); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -1,688 +1,245 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2019 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package com.weis.darklaf.ui.menu; |
||||
|
||||
import com.weis.darklaf.util.DarkUIUtil; |
||||
import com.weis.darklaf.util.LazyActionMap; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import sun.swing.DefaultLookup; |
||||
import sun.swing.UIAction; |
||||
import sun.swing.MenuItemLayoutHelper; |
||||
import sun.swing.SwingUtilities2; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.event.ChangeEvent; |
||||
import javax.swing.event.ChangeListener; |
||||
import javax.swing.event.MenuDragMouseEvent; |
||||
import javax.swing.event.MenuDragMouseListener; |
||||
import javax.swing.event.MenuKeyEvent; |
||||
import javax.swing.event.MenuKeyListener; |
||||
import javax.swing.event.MenuListener; |
||||
import javax.swing.event.MouseInputListener; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.UIResource; |
||||
import javax.swing.plaf.basic.BasicMenuUI; |
||||
import java.awt.*; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.KeyEvent; |
||||
import java.awt.event.MouseEvent; |
||||
import java.beans.PropertyChangeEvent; |
||||
import java.beans.PropertyChangeListener; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Objects; |
||||
|
||||
/** |
||||
* Code taken from {@link BasicMenuUI} |
||||
*/ |
||||
public class DarkMenuUI extends DarkMenuItemUIBase { |
||||
public class DarkMenuUI extends BasicMenuUI { |
||||
|
||||
/* diagnostic aids -- should be false for production builds. */ |
||||
private static final boolean TRACE = false; // trace creates and disposes
|
||||
private static final boolean VERBOSE = false; // show reuse hits/misses
|
||||
private static final boolean DEBUG = false; // show bad params, misc.
|
||||
private static boolean crossMenuMnemonic = true; |
||||
/** |
||||
* The instance of {@code ChangeListener}. |
||||
*/ |
||||
protected ChangeListener changeListener; |
||||
/** |
||||
* The instance of {@code MenuListener}. |
||||
*/ |
||||
protected MenuListener menuListener; |
||||
private int lastMnemonic = 0; |
||||
/** |
||||
* Uses as the parent of the windowInputMap when selected. |
||||
*/ |
||||
private InputMap selectedWindowInputMap; |
||||
protected Icon arrowIconHover; |
||||
|
||||
/** |
||||
* Constructs a new instance of {@code BasicMenuUI}. |
||||
* |
||||
* @param x a component |
||||
* @return a new instance of {@code BasicMenuUI} |
||||
*/ |
||||
@NotNull |
||||
@Contract(value = "_ -> new", pure = true) |
||||
public static ComponentUI createUI(final JComponent x) { |
||||
return new DarkMenuUI(); |
||||
} |
||||
|
||||
protected static void loadActionMap(@NotNull final LazyActionMap map) { |
||||
loadActionMap(map); |
||||
map.put(new Actions(Actions.SELECT, null, true)); |
||||
} |
||||
|
||||
private static void appendPath(@NotNull final MenuElement[] path, final MenuElement elem) { |
||||
MenuElement[] newPath = new MenuElement[path.length + 1]; |
||||
System.arraycopy(path, 0, newPath, 0, path.length); |
||||
newPath[path.length] = elem; |
||||
MenuSelectionManager.defaultManager().setSelectedPath(newPath); |
||||
} |
||||
|
||||
@NotNull |
||||
protected static java.util.List<JPopupMenu> getPopups() { |
||||
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
||||
MenuElement[] p = msm.getSelectedPath(); |
||||
|
||||
java.util.List<JPopupMenu> list = new ArrayList<JPopupMenu>(p.length); |
||||
for (MenuElement element : p) { |
||||
if (element instanceof JPopupMenu) { |
||||
list.add((JPopupMenu) element); |
||||
} |
||||
} |
||||
return list; |
||||
} |
||||
|
||||
protected static JPopupMenu getLastPopup() { |
||||
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
||||
MenuElement[] p = msm.getSelectedPath(); |
||||
JPopupMenu popup = null; |
||||
|
||||
for (int i = p.length - 1; popup == null && i >= 0; i--) { |
||||
if (p[i] instanceof JPopupMenu) { popup = (JPopupMenu) p[i]; } |
||||
} |
||||
return popup; |
||||
} |
||||
|
||||
@Override |
||||
protected void installDefaults() { |
||||
super.installDefaults(); |
||||
updateDefaultBackgroundColor(); |
||||
((JMenu) menuItem).setDelay(200); |
||||
crossMenuMnemonic = UIManager.getBoolean("Menu.crossMenuMnemonic"); |
||||
} |
||||
|
||||
protected String getPropertyPrefix() { |
||||
return "Menu"; |
||||
} |
||||
|
||||
protected void installListeners() { |
||||
super.installListeners(); |
||||
|
||||
if (changeListener == null) { changeListener = createChangeListener(menuItem); } |
||||
|
||||
if (changeListener != null) { menuItem.addChangeListener(changeListener); } |
||||
|
||||
if (menuListener == null) { menuListener = createMenuListener(menuItem); } |
||||
|
||||
if (menuListener != null) { ((JMenu) menuItem).addMenuListener(menuListener); } |
||||
} |
||||
|
||||
protected void installKeyboardActions() { |
||||
super.installKeyboardActions(); |
||||
updateMnemonicBinding(); |
||||
} |
||||
|
||||
@SuppressWarnings("deprecation") |
||||
void updateMnemonicBinding() { |
||||
int mnemonic = menuItem.getModel().getMnemonic(); |
||||
int[] shortcutKeys = (int[]) DefaultLookup.get(menuItem, this, |
||||
"Menu.shortcutKeys"); |
||||
if (shortcutKeys == null) { |
||||
shortcutKeys = new int[]{KeyEvent.ALT_MASK, |
||||
KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK}; |
||||
} |
||||
if (mnemonic == lastMnemonic) { |
||||
return; |
||||
} |
||||
InputMap windowInputMap = SwingUtilities.getUIInputMap( |
||||
menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); |
||||
if (lastMnemonic != 0 && windowInputMap != null) { |
||||
for (int shortcutKey : shortcutKeys) { |
||||
windowInputMap.remove(KeyStroke.getKeyStroke |
||||
(lastMnemonic, shortcutKey, false)); |
||||
} |
||||
} |
||||
if (mnemonic != 0) { |
||||
if (windowInputMap == null) { |
||||
windowInputMap = createInputMap(JComponent. |
||||
WHEN_IN_FOCUSED_WINDOW); |
||||
SwingUtilities.replaceUIInputMap(menuItem, JComponent. |
||||
WHEN_IN_FOCUSED_WINDOW, windowInputMap); |
||||
} |
||||
for (int shortcutKey : shortcutKeys) { |
||||
windowInputMap.put(KeyStroke.getKeyStroke(mnemonic, |
||||
shortcutKey, false), "selectMenu"); |
||||
} |
||||
} |
||||
lastMnemonic = mnemonic; |
||||
} |
||||
|
||||
protected void uninstallDefaults() { |
||||
menuItem.setArmed(false); |
||||
menuItem.setSelected(false); |
||||
menuItem.resetKeyboardActions(); |
||||
super.uninstallDefaults(); |
||||
acceleratorFont = UIManager.getFont("MenuItem.font"); |
||||
acceleratorForeground = UIManager.getColor("MenuItem.foreground"); |
||||
acceleratorSelectionForeground = UIManager.getColor("MenuItem.selectionForeground"); |
||||
arrowIconHover = UIManager.getIcon("MenuItem.arrowHover.icon"); |
||||
} |
||||
|
||||
protected void uninstallListeners() { |
||||
super.uninstallListeners(); |
||||
|
||||
if (changeListener != null) { menuItem.removeChangeListener(changeListener); } |
||||
|
||||
if (menuListener != null) { ((JMenu) menuItem).removeMenuListener(menuListener); } |
||||
|
||||
changeListener = null; |
||||
menuListener = null; |
||||
handler = null; |
||||
} |
||||
|
||||
protected void uninstallKeyboardActions() { |
||||
super.uninstallKeyboardActions(); |
||||
lastMnemonic = 0; |
||||
} |
||||
|
||||
protected MouseInputListener createMouseInputListener(final JComponent c) { |
||||
return getHandler(); |
||||
} |
||||
|
||||
protected DarkMenuItemUIBase.Handler getHandler() { |
||||
if (handler == null) { |
||||
handler = new DarkMenuUI.Handler(); |
||||
} |
||||
return handler; |
||||
} |
||||
|
||||
protected MenuDragMouseListener createMenuDragMouseListener(final JComponent c) { |
||||
return getHandler(); |
||||
} |
||||
|
||||
protected MenuKeyListener createMenuKeyListener(final JComponent c) { |
||||
return (MenuKeyListener) getHandler(); |
||||
} |
||||
|
||||
protected PropertyChangeListener createPropertyChangeListener(final JComponent c) { |
||||
return getHandler(); |
||||
} |
||||
|
||||
public Dimension getMinimumSize(final JComponent c) { |
||||
return (((JMenu) menuItem).isTopLevelMenu()) ? |
||||
c.getPreferredSize() : null; |
||||
@Override |
||||
public void paint(final Graphics g, final JComponent c) { |
||||
paintMenuItem(g, c, checkIcon, getArrowIcon(), |
||||
selectionBackground, selectionForeground, |
||||
defaultTextIconGap); |
||||
} |
||||
|
||||
public Dimension getMaximumSize(final JComponent c) { |
||||
if (((JMenu) menuItem).isTopLevelMenu()) { |
||||
Dimension d = c.getPreferredSize(); |
||||
return new Dimension(d.width, Short.MAX_VALUE); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Returns an instance of {@code ChangeListener}. |
||||
* |
||||
* @param c a component |
||||
* @return an instance of {@code ChangeListener} |
||||
*/ |
||||
protected ChangeListener createChangeListener(final JComponent c) { |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Returns an instance of {@code MenuListener}. |
||||
* |
||||
* @param c a component |
||||
* @return an instance of {@code MenuListener} |
||||
*/ |
||||
protected MenuListener createMenuListener(final JComponent c) { |
||||
return null; |
||||
protected Icon getArrowIcon() { |
||||
boolean hover = menuItem.getModel().isArmed() |
||||
|| (menuItem instanceof JMenu && menuItem.getModel().isSelected()); |
||||
return hover ? arrowIconHover : arrowIcon; |
||||
} |
||||
|
||||
/* |
||||
* Set the background color depending on whether this is a toplevel menu |
||||
* in a menubar or a submenu of another menu. |
||||
*/ |
||||
private void updateDefaultBackgroundColor() { |
||||
if (!UIManager.getBoolean("Menu.useMenuBarBackgroundForTopLevel")) { |
||||
return; |
||||
} |
||||
JMenu menu = (JMenu) menuItem; |
||||
if (menu.getBackground() instanceof UIResource) { |
||||
if (menu.isTopLevelMenu()) { |
||||
menu.setBackground(UIManager.getColor("MenuBar.background")); |
||||
protected void paintCheckIcon(final Graphics g, @NotNull final MenuItemLayoutHelper lh, |
||||
final MenuItemLayoutHelper.LayoutResult lr, |
||||
final Color holdc, final Color foreground) { |
||||
if (lh.getCheckIcon() != null) { |
||||
ButtonModel model = lh.getMenuItem().getModel(); |
||||
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu |
||||
&& model.isSelected())) { |
||||
g.setColor(foreground); |
||||
} else { |
||||
menu.setBackground(UIManager.getColor(getPropertyPrefix() + ".background")); |
||||
g.setColor(holdc); |
||||
} |
||||
if (lh.useCheckAndArrow()) { |
||||
lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, |
||||
lr.getCheckRect().x, lr.getCheckRect().y); |
||||
} |
||||
g.setColor(holdc); |
||||
} |
||||
} |
||||
|
||||
void installLazyActionMap() { |
||||
LazyActionMap.installLazyActionMap(menuItem, BasicMenuUI.class, |
||||
getPropertyPrefix() + ".actionMap"); |
||||
} |
||||
|
||||
/** |
||||
* Sets timer to the {@code menu}. |
||||
* |
||||
* @param menu an instance of {@code JMenu}. |
||||
*/ |
||||
protected void setupPostTimer(@NotNull final JMenu menu) { |
||||
Timer timer = new Timer(menu.getDelay(), new Actions(Actions.SELECT, menu, false)); |
||||
timer.setRepeats(false); |
||||
timer.start(); |
||||
} |
||||
|
||||
private static class Actions extends UIAction { |
||||
private static final String SELECT = "selectMenu"; |
||||
|
||||
// NOTE: This will be null if the action is registered in the
|
||||
// ActionMap. For the timer use it will be non-null.
|
||||
private JMenu menu; |
||||
private boolean force = false; |
||||
|
||||
Actions(final String key, final JMenu menu, final boolean shouldForce) { |
||||
super(key); |
||||
this.menu = menu; |
||||
this.force = shouldForce; |
||||
} |
||||
|
||||
public void actionPerformed(final ActionEvent e) { |
||||
JMenu menu = getMenu(e); |
||||
if (!crossMenuMnemonic) { |
||||
JPopupMenu pm = getLastPopup(); |
||||
if (pm != null && pm != menu.getParent()) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
final MenuSelectionManager defaultManager = MenuSelectionManager.defaultManager(); |
||||
if (force) { |
||||
Container cnt = menu.getParent(); |
||||
if (cnt instanceof JMenuBar) { |
||||
MenuElement[] me; |
||||
MenuElement[] subElements; |
||||
|
||||
subElements = menu.getPopupMenu().getSubElements(); |
||||
if (subElements.length > 0) { |
||||
me = new MenuElement[4]; |
||||
me[0] = (MenuElement) cnt; |
||||
me[1] = menu; |
||||
me[2] = menu.getPopupMenu(); |
||||
me[3] = subElements[0]; |
||||
} else { |
||||
me = new MenuElement[3]; |
||||
me[0] = (MenuElement) cnt; |
||||
me[1] = menu; |
||||
me[2] = menu.getPopupMenu(); |
||||
} |
||||
defaultManager.setSelectedPath(me); |
||||
protected void paintIcon(final Graphics g, @NotNull final MenuItemLayoutHelper lh, |
||||
final MenuItemLayoutHelper.LayoutResult lr, final Color holdc) { |
||||
if (lh.getIcon() != null) { |
||||
Icon icon; |
||||
ButtonModel model = lh.getMenuItem().getModel(); |
||||
if (!model.isEnabled()) { |
||||
icon = lh.getMenuItem().getDisabledIcon(); |
||||
} else if (model.isPressed() && model.isArmed()) { |
||||
icon = lh.getMenuItem().getPressedIcon(); |
||||
if (icon == null) { |
||||
// Use default icon
|
||||
icon = lh.getMenuItem().getIcon(); |
||||
} |
||||
} else { |
||||
MenuElement[] path = defaultManager.getSelectedPath(); |
||||
if (path.length > 0 && path[path.length - 1] == menu) { |
||||
appendPath(path, menu.getPopupMenu()); |
||||
} |
||||
icon = lh.getMenuItem().getIcon(); |
||||
} |
||||
} |
||||
|
||||
private JMenu getMenu(final ActionEvent e) { |
||||
if (e.getSource() instanceof JMenu) { |
||||
return (JMenu) e.getSource(); |
||||
if (icon != null) { |
||||
icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, lr.getIconRect().y); |
||||
g.setColor(holdc); |
||||
} |
||||
return menu; |
||||
} |
||||
|
||||
@Override |
||||
public boolean accept(final Object c) { |
||||
if (c instanceof JMenu) { |
||||
return ((JMenu) c).isEnabled(); |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Instantiated and used by a menu item to handle the current menu selection |
||||
* from mouse events. A MouseInputHandler processes and forwards all mouse events |
||||
* to a shared instance of the MenuSelectionManager. |
||||
* <p> |
||||
* This class is protected so that it can be subclassed by other look and |
||||
* feels to implement their own mouse handling behavior. All overridden |
||||
* methods should call the parent methods so that the menu selection |
||||
* is correct. |
||||
* |
||||
* @see javax.swing.MenuSelectionManager |
||||
* @since 1.4 |
||||
*/ |
||||
protected class MouseInputHandler implements MouseInputListener { |
||||
// NOTE: This class exists only for backward compatibility. All
|
||||
// its functionality has been moved into Handler. If you need to add
|
||||
// new functionality add it to the Handler, but make sure this
|
||||
// class calls into the Handler.
|
||||
|
||||
public void mouseClicked(final MouseEvent e) { |
||||
getHandler().mouseClicked(e); |
||||
} |
||||
|
||||
/** |
||||
* Invoked when the mouse has been clicked on the menu. This |
||||
* method clears or sets the selection path of the |
||||
* MenuSelectionManager. |
||||
* |
||||
* @param e the mouse event |
||||
*/ |
||||
public void mousePressed(final MouseEvent e) { |
||||
getHandler().mousePressed(e); |
||||
} |
||||
|
||||
/** |
||||
* Invoked when the mouse has been released on the menu. Delegates the |
||||
* mouse event to the MenuSelectionManager. |
||||
* |
||||
* @param e the mouse event |
||||
*/ |
||||
public void mouseReleased(final MouseEvent e) { |
||||
getHandler().mouseReleased(e); |
||||
} |
||||
|
||||
/** |
||||
* Invoked when the cursor enters the menu. This method sets the selected |
||||
* path for the MenuSelectionManager and handles the case |
||||
* in which a menu item is used to pop up an additional menu, as in a |
||||
* hierarchical menu system. |
||||
* |
||||
* @param e the mouse event; not used |
||||
*/ |
||||
public void mouseEntered(final MouseEvent e) { |
||||
getHandler().mouseEntered(e); |
||||
} |
||||
|
||||
public void mouseExited(final MouseEvent e) { |
||||
getHandler().mouseExited(e); |
||||
} |
||||
|
||||
/** |
||||
* Invoked when a mouse button is pressed on the menu and then dragged. |
||||
* Delegates the mouse event to the MenuSelectionManager. |
||||
* |
||||
* @param e the mouse event |
||||
* @see java.awt.event.MouseMotionListener#mouseDragged |
||||
*/ |
||||
public void mouseDragged(final MouseEvent e) { |
||||
getHandler().mouseDragged(e); |
||||
} |
||||
|
||||
public void mouseMoved(final MouseEvent e) { |
||||
getHandler().mouseMoved(e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* As of Java 2 platform 1.4, this previously undocumented class
|
||||
* is now obsolete. KeyBindings are now managed by the popup menu. |
||||
*/ |
||||
public class ChangeHandler implements ChangeListener { |
||||
/** |
||||
* The instance of {@code JMenu}. |
||||
*/ |
||||
public JMenu menu; |
||||
|
||||
/** |
||||
* The instance of {@code BasicMenuUI}. |
||||
*/ |
||||
public BasicMenuUI ui; |
||||
|
||||
/** |
||||
* {@code true} if an item of popup menu is selected. |
||||
*/ |
||||
public boolean isSelected = false; |
||||
|
||||
/** |
||||
* The component that was focused. |
||||
*/ |
||||
public Component wasFocused; |
||||
|
||||
/** |
||||
* Constructs a new instance of {@code ChangeHandler}. |
||||
* |
||||
* @param m an instance of {@code JMenu} |
||||
* @param ui an instance of {@code BasicMenuUI} |
||||
*/ |
||||
public ChangeHandler(final JMenu m, final BasicMenuUI ui) { |
||||
menu = m; |
||||
this.ui = ui; |
||||
} |
||||
|
||||
public void stateChanged(final ChangeEvent e) { |
||||
protected void paintText(final Graphics g, @NotNull final MenuItemLayoutHelper lh, |
||||
final MenuItemLayoutHelper.LayoutResult lr) { |
||||
if (!lh.getText().isBlank()) { |
||||
if (lh.getHtmlView() != null) { |
||||
// Text is HTML
|
||||
lh.getHtmlView().paint(g, lr.getTextRect()); |
||||
} else { |
||||
// Text isn't HTML
|
||||
paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private class Handler extends DarkMenuItemUIBase.Handler implements MenuKeyListener { |
||||
//
|
||||
// MouseInputListener
|
||||
//
|
||||
public void mouseClicked(final MouseEvent e) { |
||||
} |
||||
|
||||
/** |
||||
* Invoked when the mouse has been clicked on the menu. This |
||||
* method clears or sets the selection path of the |
||||
* MenuSelectionManager. |
||||
* |
||||
* @param e the mouse event |
||||
*/ |
||||
public void mousePressed(final MouseEvent e) { |
||||
JMenu menu = (JMenu) menuItem; |
||||
if (!menu.isEnabled()) { return; } |
||||
|
||||
MenuSelectionManager manager = MenuSelectionManager.defaultManager(); |
||||
if (menu.isTopLevelMenu()) { |
||||
if (menu.isSelected() && menu.getPopupMenu().isShowing()) { |
||||
manager.clearSelectedPath(); |
||||
protected void paintAccText(final Graphics g, final MenuItemLayoutHelper lh, |
||||
final MenuItemLayoutHelper.LayoutResult lr) { |
||||
rightAlignAccText(lh, lr); |
||||
if (!lh.getAccText().isBlank()) { |
||||
ButtonModel model = lh.getMenuItem().getModel(); |
||||
g.setFont(lh.getAccFontMetrics().getFont()); |
||||
if (!model.isEnabled()) { |
||||
// *** paint the accText disabled
|
||||
if (disabledForeground != null) { |
||||
g.setColor(disabledForeground); |
||||
SwingUtilities2.drawString(lh.getMenuItem(), g, |
||||
lh.getAccText(), lr.getAccRect().x, |
||||
lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); |
||||
} else { |
||||
Container cnt = menu.getParent(); |
||||
if (cnt instanceof JMenuBar) { |
||||
MenuElement[] me = new MenuElement[2]; |
||||
me[0] = (MenuElement) cnt; |
||||
me[1] = menu; |
||||
manager.setSelectedPath(me); |
||||
} |
||||
g.setColor(lh.getMenuItem().getBackground().brighter()); |
||||
SwingUtilities2.drawString(lh.getMenuItem(), g, |
||||
lh.getAccText(), lr.getAccRect().x, |
||||
lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); |
||||
g.setColor(lh.getMenuItem().getBackground().darker()); |
||||
SwingUtilities2.drawString(lh.getMenuItem(), g, |
||||
lh.getAccText(), lr.getAccRect().x - 1, |
||||
lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); |
||||
} |
||||
} |
||||
|
||||
MenuElement[] selectedPath = manager.getSelectedPath(); |
||||
if (selectedPath.length > 0 && selectedPath[selectedPath.length - 1] != menu.getPopupMenu()) { |
||||
if (menu.isTopLevelMenu() || menu.getDelay() == 0) { |
||||
appendPath(selectedPath, menu.getPopupMenu()); |
||||
} else { |
||||
// *** paint the accText normally
|
||||
if (model.isArmed() |
||||
|| (lh.getMenuItem() instanceof JMenu |
||||
&& model.isSelected())) { |
||||
g.setColor(acceleratorSelectionForeground); |
||||
} else { |
||||
setupPostTimer(menu); |
||||
g.setColor(acceleratorForeground); |
||||
} |
||||
SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), |
||||
lr.getAccRect().x, lr.getAccRect().y + |
||||
lh.getAccFontMetrics().getAscent()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Invoked when the mouse has been released on the menu. Delegates the |
||||
* mouse event to the MenuSelectionManager. |
||||
* |
||||
* @param e the mouse event |
||||
*/ |
||||
public void mouseReleased(final MouseEvent e) { |
||||
JMenu menu = (JMenu) menuItem; |
||||
if (!menu.isEnabled()) { return; } |
||||
MenuSelectionManager manager = MenuSelectionManager.defaultManager(); |
||||
manager.processMouseEvent(e); |
||||
if (!e.isConsumed()) { manager.clearSelectedPath(); } |
||||
} |
||||
|
||||
/** |
||||
* Invoked when the cursor enters the menu. This method sets the selected |
||||
* path for the MenuSelectionManager and handles the case |
||||
* in which a menu item is used to pop up an additional menu, as in a |
||||
* hierarchical menu system. |
||||
* |
||||
* @param e the mouse event; not used |
||||
*/ |
||||
public void mouseEntered(final MouseEvent e) { |
||||
JMenu menu = (JMenu) menuItem; |
||||
// only disable the menu highlighting if it's disabled and the property isn't
|
||||
// true. This allows disabled rollovers to work in WinL&F
|
||||
if (!menu.isEnabled() && !UIManager.getBoolean("MenuItem.disabledAreNavigable")) { |
||||
return; |
||||
protected void paintArrowIcon(final Graphics g, @NotNull final MenuItemLayoutHelper lh, |
||||
final MenuItemLayoutHelper.LayoutResult lr, |
||||
final Color foreground) { |
||||
if (lh.getArrowIcon() != null) { |
||||
ButtonModel model = lh.getMenuItem().getModel(); |
||||
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu |
||||
&& model.isSelected())) { |
||||
g.setColor(foreground); |
||||
} |
||||
|
||||
MenuSelectionManager manager = MenuSelectionManager.defaultManager(); |
||||
MenuElement[] selectedPath = manager.getSelectedPath(); |
||||
if (!menu.isTopLevelMenu()) { |
||||
if (!(selectedPath.length > 0 && selectedPath[selectedPath.length - 1] == menu.getPopupMenu())) { |
||||
if (menu.getDelay() == 0) { |
||||
appendPath(getPath(), menu.getPopupMenu()); |
||||
} else { |
||||
manager.setSelectedPath(getPath()); |
||||
setupPostTimer(menu); |
||||
} |
||||
} |
||||
} else { |
||||
if (selectedPath.length > 0 && selectedPath[0] == menu.getParent()) { |
||||
MenuElement[] newPath = new MenuElement[3]; |
||||
// A top level menu's parent is by definition
|
||||
// a JMenuBar
|
||||
newPath[0] = (MenuElement) menu.getParent(); |
||||
newPath[1] = menu; |
||||
if (getLastPopup() != null) { |
||||
newPath[2] = menu.getPopupMenu(); |
||||
} |
||||
manager.setSelectedPath(newPath); |
||||
} |
||||
if (lh.useCheckAndArrow()) { |
||||
lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, |
||||
lr.getArrowRect().x, lr.getArrowRect().y); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void mouseExited(final MouseEvent e) { |
||||
} |
||||
|
||||
/** |
||||
* Invoked when a mouse button is pressed on the menu and then dragged. |
||||
* Delegates the mouse event to the MenuSelectionManager. |
||||
* |
||||
* @param e the mouse event |
||||
* @see java.awt.event.MouseMotionListener#mouseDragged |
||||
*/ |
||||
public void mouseDragged(final MouseEvent e) { |
||||
JMenu menu = (JMenu) menuItem; |
||||
if (!menu.isEnabled()) { return; } |
||||
MenuSelectionManager.defaultManager().processMouseEvent(e); |
||||
private static void rightAlignAccText(@NotNull final MenuItemLayoutHelper lh, |
||||
@NotNull final MenuItemLayoutHelper.LayoutResult lr) { |
||||
var accRect = lr.getAccRect(); |
||||
ButtonModel model = lh.getMenuItem().getModel(); |
||||
if (model.isEnabled()) { |
||||
accRect.x = lh.getViewRect().x + lh.getViewRect().width |
||||
- lh.getMenuItem().getIconTextGap() - lr.getAccRect().width; |
||||
} |
||||
} |
||||
|
||||
public void mouseMoved(final MouseEvent e) { |
||||
} |
||||
protected void paintMenuItem(@NotNull final Graphics g, final JComponent c, |
||||
final Icon checkIcon, final Icon arrowIcon, |
||||
final Color background, final Color foreground, |
||||
final int defaultTextIconGap) { |
||||
// Save original graphics font and color
|
||||
Font holdf = g.getFont(); |
||||
Color holdc = g.getColor(); |
||||
|
||||
//
|
||||
// MenuDragHandler
|
||||
//
|
||||
public void menuDragMouseEntered(final MenuDragMouseEvent e) { |
||||
} |
||||
JMenuItem mi = (JMenuItem) c; |
||||
g.setFont(mi.getFont()); |
||||
|
||||
public void menuDragMouseDragged(final MenuDragMouseEvent e) { |
||||
if (!menuItem.isEnabled()) return; |
||||
Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); |
||||
DarkUIUtil.applyInsets(viewRect, mi.getInsets()); |
||||
|
||||
MenuSelectionManager manager = e.getMenuSelectionManager(); |
||||
MenuElement[] path = e.getPath(); |
||||
MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, |
||||
arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, |
||||
mi.getComponentOrientation().isLeftToRight(), mi.getFont(), |
||||
acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), |
||||
getPropertyPrefix()); |
||||
MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); |
||||
|
||||
Point p = e.getPoint(); |
||||
if (p.x >= 0 && p.x < menuItem.getWidth() && |
||||
p.y >= 0 && p.y < menuItem.getHeight()) { |
||||
JMenu menu = (JMenu) menuItem; |
||||
MenuElement[] selectedPath = manager.getSelectedPath(); |
||||
if (!(selectedPath.length > 0 && |
||||
selectedPath[selectedPath.length - 1] == |
||||
menu.getPopupMenu())) { |
||||
if (menu.isTopLevelMenu() || |
||||
menu.getDelay() == 0 || |
||||
e.getID() == MouseEvent.MOUSE_DRAGGED) { |
||||
appendPath(path, menu.getPopupMenu()); |
||||
} else { |
||||
manager.setSelectedPath(path); |
||||
setupPostTimer(menu); |
||||
} |
||||
} |
||||
} else if (e.getID() == MouseEvent.MOUSE_RELEASED) { |
||||
Component comp = manager.componentForPoint(e.getComponent(), e.getPoint()); |
||||
if (comp == null) { manager.clearSelectedPath(); } |
||||
} |
||||
|
||||
} |
||||
paintBackground(g, mi, background); |
||||
paintCheckIcon(g, lh, lr, holdc, foreground); |
||||
paintIcon(g, lh, lr, holdc); |
||||
g.setColor(foreground); |
||||
paintText(g, lh, lr); |
||||
paintAccText(g, lh, lr); |
||||
paintArrowIcon(g, lh, lr, foreground); |
||||
|
||||
public void menuDragMouseExited(final MenuDragMouseEvent e) { |
||||
} |
||||
// Restore original graphics font and color
|
||||
g.setColor(holdc); |
||||
g.setFont(holdf); |
||||
} |
||||
|
||||
public void menuDragMouseReleased(final MenuDragMouseEvent e) { |
||||
} |
||||
@Override |
||||
protected void paintBackground(@NotNull final Graphics g, @NotNull final JMenuItem menuItem, final Color bgColor) { |
||||
ButtonModel model = menuItem.getModel(); |
||||
Color oldColor = g.getColor(); |
||||
int menuWidth = menuItem.getWidth(); |
||||
int menuHeight = menuItem.getHeight() + 1; |
||||
|
||||
//
|
||||
// PropertyChangeListener
|
||||
//
|
||||
public void propertyChange(final PropertyChangeEvent e) { |
||||
if (Objects.equals(e.getPropertyName(), AbstractButton. |
||||
MNEMONIC_CHANGED_PROPERTY)) { |
||||
updateMnemonicBinding(); |
||||
boolean parentOpaque = menuItem.getParent().isOpaque(); |
||||
if (menuItem.isOpaque() && parentOpaque) { |
||||
if (model.isArmed() || (menuItem instanceof JMenu && model.isSelected())) { |
||||
g.setColor(bgColor); |
||||
g.fillRect(0, 0, menuWidth, menuHeight); |
||||
} else { |
||||
if (e.getPropertyName().equals("ancestor")) { |
||||
updateDefaultBackgroundColor(); |
||||
} |
||||
super.propertyChange(e); |
||||
} |
||||
} |
||||
|
||||
//
|
||||
// MenuKeyListener
|
||||
//
|
||||
|
||||
/** |
||||
* Open the Menu |
||||
*/ |
||||
public void menuKeyTyped(final MenuKeyEvent e) { |
||||
if (!crossMenuMnemonic && getLastPopup() != null) { |
||||
// when crossMenuMnemonic is not set, we don't open a toplevel
|
||||
// menu if another toplevel menu is already open
|
||||
return; |
||||
} |
||||
|
||||
if (getPopups().size() != 0) { |
||||
//Fix 6939261: to return in case not on the main menu
|
||||
//and has a pop-up.
|
||||
//after return code will be handled in BasicPopupMenuUI.java
|
||||
return; |
||||
} |
||||
|
||||
char key = Character.toLowerCase((char) menuItem.getMnemonic()); |
||||
MenuElement[] path = e.getPath(); |
||||
if (key == Character.toLowerCase(e.getKeyChar())) { |
||||
JPopupMenu popupMenu = ((JMenu) menuItem).getPopupMenu(); |
||||
ArrayList<MenuElement> newList = new ArrayList<>(Arrays.asList(path)); |
||||
newList.add(popupMenu); |
||||
MenuElement[] subs = popupMenu.getSubElements(); |
||||
MenuElement sub = DarkUIUtil.findEnabledChild(subs, -1, true); |
||||
if (sub != null) { |
||||
newList.add(sub); |
||||
} |
||||
MenuSelectionManager manager = e.getMenuSelectionManager(); |
||||
MenuElement[] newPath = new MenuElement[0]; |
||||
newPath = newList.toArray(newPath); |
||||
manager.setSelectedPath(newPath); |
||||
e.consume(); |
||||
} |
||||
} |
||||
|
||||
public void menuKeyPressed(final MenuKeyEvent e) { |
||||
} |
||||
|
||||
public void menuKeyReleased(final MenuKeyEvent e) { |
||||
g.setColor(menuItem.getBackground()); |
||||
g.fillRect(0, 0, menuWidth, menuHeight); |
||||
} |
||||
g.setColor(oldColor); |
||||
} else if (model.isArmed() || (menuItem instanceof JMenu && |
||||
model.isSelected())) { |
||||
g.setColor(bgColor); |
||||
g.fillRect(0, 0, menuWidth, menuHeight); |
||||
g.setColor(oldColor); |
||||
} |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 438 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 226 B |
After Width: | Height: | Size: 226 B |
After Width: | Height: | Size: 170 B |
After Width: | Height: | Size: 169 B |
After Width: | Height: | Size: 438 B |
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 200 B |
After Width: | Height: | Size: 203 B |
After Width: | Height: | Size: 200 B |
After Width: | Height: | Size: 226 B |
After Width: | Height: | Size: 223 B |
After Width: | Height: | Size: 169 B |
After Width: | Height: | Size: 166 B |