Skip to content

Commit 7be4d71

Browse files
committed
small improvements
1 parent 9b3a6a6 commit 7be4d71

14 files changed

Lines changed: 103 additions & 60 deletions

src/main/java/pixelitor/filters/gui/PresetOwner.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ default Action createSavePresetAction(Component parent,
115115
savePreset(parent, menuAdder, menuRemover));
116116
}
117117

118-
private void savePreset(Component parent, Consumer<UserPreset> menuAdder, Consumer<UserPreset> menuRemover) {
118+
private void savePreset(Component parent,
119+
Consumer<UserPreset> menuAdder,
120+
Consumer<UserPreset> menuRemover) {
119121
String presetName = Dialogs.showInputDialog(
120122
parent, "Preset Name", "Preset Name:");
121123
if (presetName == null || presetName.isBlank()) {
@@ -130,11 +132,12 @@ private void savePreset(Component parent, Consumer<UserPreset> menuAdder, Consum
130132
if (!overwrite) {
131133
return;
132134
}
133-
// remove the overwritten preset from the menus
135+
// if the user overwrites an existing preset,
136+
// remove the old menu item before adding the new one
134137
menuRemover.accept(preset);
135138
}
136139
preset.save();
137-
menuAdder.accept(preset);
140+
menuAdder.accept(preset); // add the new preset to the menu
138141
}
139142

140143
private static boolean confirmOverwrite(Component parent, String presetName) {

src/main/java/pixelitor/filters/gui/UserPreset.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,8 @@ public Action createAction(PresetOwner owner) {
314314
}
315315

316316
/**
317-
* Detects all presets in the given directory by listing files.
318-
* The presets' contents are not loaded into memory.
317+
* Detects all presets in the given directory by listing files
318+
* ending in ".txt". The presets' contents are not loaded into memory.
319319
*/
320320
public static List<UserPreset> detectPresetNames(String presetDirName) {
321321
File presetsDir = getSaveDir(presetDirName);

src/main/java/pixelitor/gui/PixelitorWindow.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,19 @@ private PixelitorWindow() {
7373
workSpace = new WorkSpace();
7474

7575
Dimension screenSize = Screens.getMaxWindowSize();
76+
AppPreferences.loadFramePreferences(this, screenSize);
7677

7778
addMenuBar();
7879
addImageArea();
7980
addSidePanel();
81+
addStatusBar();
8082
addToolsPanel(screenSize);
8183
Tools.setDefaultTool();
82-
addStatusBar();
8384

8485
initIcons();
8586

8687
GlobalEvents.init();
8788

88-
initWindow(screenSize);
89-
}
90-
91-
private void initWindow(Dimension screenSize) {
92-
AppPreferences.loadFramePreferences(this, screenSize);
9389
if (JVM.isWindows) {
9490
// this is tricky code that had problems on Linux
9591
setupRememberingLastBounds();

src/main/java/pixelitor/tools/Tool.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -348,15 +348,6 @@ public char getHotkey() {
348348
return hotkey;
349349
}
350350

351-
/**
352-
* Returns whether this tool shares its hotkey with other tools.
353-
* A tool shares its key when it's part of a tool group where
354-
* multiple tools can be activated by the same keyboard shortcut.
355-
*/
356-
public boolean hasSharedHotkey() {
357-
return false;
358-
}
359-
360351
public boolean hasPixelSnapping() {
361352
return pixelSnapping;
362353
}

src/main/java/pixelitor/tools/Tools.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
import java.awt.event.MouseEvent;
4646
import java.awt.geom.AffineTransform;
47+
import java.util.List;
4748

4849
/**
4950
* Tool-related static utility methods
@@ -235,6 +236,17 @@ public static void maskEditingChanged(boolean maskEditing) {
235236
activeTool.maskEditingChanged(maskEditing);
236237
}
237238

239+
/**
240+
* Returns the tool groups where a single hotkey is used to cycle through the tools.
241+
*/
242+
public static List<Tool[]> getSharedHotkeyGroups() {
243+
return List.of(
244+
new Tool[]{RECTANGLE_SELECTION, ELLIPSE_SELECTION}, // M
245+
new Tool[]{LASSO_SELECTION, POLY_SELECTION}, // L
246+
new Tool[]{PEN, NODE, TRANSFORM_PATH} // P
247+
);
248+
}
249+
238250
public static class EventDispatcher {
239251
private static boolean mouseDown = false;
240252
private static PMouseEvent lastEvent;

src/main/java/pixelitor/tools/gui/ToolButton.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public ToolButton(Tool tool) {
5959
setToolTipText("<html>" + tool.getName()
6060
+ " (<b>" + tool.getHotkey() + "</b>)");
6161

62-
// Adds a listener to activate the tool when the button is selected.
62+
// Activates the tool when the button is selected.
6363
// An item listener is better than an action listener because it
6464
// is also triggered by keyboard focus traversal selections.
6565
addItemListener(e -> {
@@ -75,6 +75,7 @@ public ToolButton(Tool tool) {
7575

7676
@Override
7777
public void updateUI() {
78+
// this method can be called by the super constructor before 'tool' is initialized
7879
if (tool != null) { // changing the theme
7980
setupIcons(tool);
8081
}
@@ -91,6 +92,10 @@ private void setupIcons(Tool tool) {
9192
setSelectedIcon(selectedIcon);
9293
}
9394

95+
/**
96+
* Creates a right-click popup menu that can be used to save, load,
97+
* and manage configuration presets for the tool represented by this button.
98+
*/
9499
private void initPresetsPopup(Tool tool) {
95100
List<UserPreset> startupPresets = UserPreset.detectPresetNames(tool.getPresetDirName());
96101
numPresets = startupPresets.size();
@@ -106,6 +111,7 @@ private void initPresetsPopup(Tool tool) {
106111
presetsMenu.add(tool.createSavePresetAction(this,
107112
this::addPresetMenuItem, this::removePresetMenuItem));
108113

114+
// if any existing presets were detected, add them to the menu
109115
if (!startupPresets.isEmpty()) {
110116
if (GUIUtils.CAN_USE_FILE_MANAGER) {
111117
presetsMenu.add(tool.createManagePresetsAction());
@@ -119,17 +125,28 @@ private void initPresetsPopup(Tool tool) {
119125
setComponentPopupMenu(presetsMenu);
120126
}
121127

128+
/**
129+
* Callback to dynamically add a new menu item to the presets
130+
* menu after a user has successfully saved a new preset.
131+
*/
122132
private void addPresetMenuItem(UserPreset preset) {
123133
if (numPresets == 0) {
134+
// if this is the very first preset being added, then this
135+
// was not added during initialization, so add it now
124136
if (GUIUtils.CAN_USE_FILE_MANAGER) {
125137
presetsMenu.add(tool.createManagePresetsAction());
126138
}
127139
presetsMenu.addSeparator();
128140
}
141+
129142
presetsMenu.add(preset.createAction(tool));
130143
numPresets++;
131144
}
132145

146+
/**
147+
* Callback to dynamically remove the old menu item
148+
* when the user overwrites an existing preset.
149+
*/
133150
private void removePresetMenuItem(UserPreset preset) {
134151
Component[] menuComponents = presetsMenu.getComponents();
135152
for (Component item : menuComponents) {

src/main/java/pixelitor/tools/gui/ToolsPanel.java

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import pixelitor.colors.FgBgColors;
2222
import pixelitor.gui.GlobalEvents;
2323
import pixelitor.gui.PixelitorWindow;
24+
import pixelitor.gui.StatusBar;
25+
import pixelitor.gui.WorkSpace;
2426
import pixelitor.gui.utils.TaskAction;
2527
import pixelitor.layers.AddTextLayerAction;
2628
import pixelitor.tools.Tool;
@@ -30,6 +32,10 @@
3032
import java.awt.BorderLayout;
3133
import java.awt.Dimension;
3234
import java.awt.event.ActionEvent;
35+
import java.util.Collections;
36+
import java.util.HashSet;
37+
import java.util.List;
38+
import java.util.Set;
3339

3440
/**
3541
* The panel with the tool buttons and the color selector
@@ -38,41 +44,69 @@ public class ToolsPanel extends JPanel {
3844
public ToolsPanel(PixelitorWindow pw, Dimension screenSize) {
3945
Dimension buttonSize = calcToolButtonSize(screenSize, pw);
4046

41-
// We need to give a hint to the layout manager, but at
42-
// this point neither the panel nor the window size is known.
43-
int heightHint = Math.max(screenSize.height - 168, 0);
47+
JComponent colorSelector = createColorSelector(pw);
48+
int heightHint = calcHeightHint(pw, colorSelector, buttonSize);
4449

4550
JPanel buttonsPanel = new JPanel(new ToolButtonsLayout(buttonSize.width, buttonSize.height, 0, heightHint));
4651
addToolButtons(buttonsPanel);
4752

4853
setLayout(new BorderLayout());
4954
add(buttonsPanel, BorderLayout.CENTER);
50-
addColorSelector(pw);
55+
add(colorSelector, BorderLayout.SOUTH);
5156

5257
setupTShortCut();
5358
}
5459

55-
private static void addToolButtons(JPanel toolsPanel) {
60+
private static int calcHeightHint(PixelitorWindow pw, JComponent colorSelector, Dimension buttonSize) {
61+
// get the preferred heights of all other components that take up vertical space.
62+
int menuBarHeight = pw.getJMenuBar().getPreferredSize().height;
63+
WorkSpace workSpace = pw.getWorkSpace();
64+
int toolSettingsHeight = workSpace.areToolsVisible() ? ToolSettingsPanelContainer.get().getPreferredSize().height : 0;
65+
int statusBarHeight = workSpace.isStatusBarVisible() ? StatusBar.get().getPreferredSize().height : 0;
66+
int colorSelectorHeight = colorSelector.getPreferredSize().height;
67+
68+
// the window's insets include the title bar
69+
int windowInsetsHeight = pw.getInsets().top + pw.getInsets().bottom;
70+
71+
// sum of all vertical space NOT available to the buttons panel
72+
int totalOtherHeight = menuBarHeight + toolSettingsHeight + statusBarHeight + colorSelectorHeight + windowInsetsHeight;
73+
74+
// the total window height minus all other components
75+
int heightHint = pw.getHeight() - totalOtherHeight;
76+
77+
// ensure the hint is a positive value
78+
heightHint = Math.max(heightHint, buttonSize.height);
79+
return heightHint;
80+
}
81+
82+
private static void addToolButtons(JPanel buttonContainer) {
5683
ButtonGroup group = new ButtonGroup();
57-
Tool[] tools = Tools.getAll();
58-
for (Tool tool : tools) {
84+
85+
List<Tool[]> sharedHotkeyGroups = Tools.getSharedHotkeyGroups();
86+
for (Tool[] toolGroup : sharedHotkeyGroups) {
87+
setupSharedHotkey(toolGroup);
88+
}
89+
90+
Set<Tool> toolsWithSharedHotkeys = new HashSet<>();
91+
for (Tool[] toolGroup : sharedHotkeyGroups) {
92+
Collections.addAll(toolsWithSharedHotkeys, toolGroup);
93+
}
94+
95+
for (Tool tool : Tools.getAll()) {
5996
ToolButton toolButton = new ToolButton(tool);
60-
toolsPanel.add(toolButton);
97+
buttonContainer.add(toolButton);
6198
group.add(toolButton);
62-
if (!tool.hasSharedHotkey()) {
99+
100+
if (!toolsWithSharedHotkeys.contains(tool)) {
63101
setupHotkey(tool);
64102
}
65103
}
66-
// manually register the hotkeys of the sharing tools
67-
setupSharedHotkey(Tools.RECTANGLE_SELECTION, Tools.ELLIPSE_SELECTION);
68-
setupSharedHotkey(Tools.LASSO_SELECTION, Tools.POLY_SELECTION);
69-
setupSharedHotkey(Tools.PEN, Tools.NODE, Tools.TRANSFORM_PATH);
70104
}
71105

72-
private void addColorSelector(PixelitorWindow pw) {
106+
private static JComponent createColorSelector(PixelitorWindow pw) {
73107
FgBgColorSelector colorSelector = new FgBgColorSelector(pw);
74108
FgBgColors.setUI(colorSelector);
75-
add(colorSelector, BorderLayout.SOUTH);
109+
return colorSelector;
76110
}
77111

78112
private static void setupTShortCut() {
@@ -103,17 +137,17 @@ private static Dimension calcToolButtonSize(Dimension screen, PixelitorWindow pw
103137
}
104138

105139
private static void setupHotkey(Tool tool) {
106-
Action activateAction = new TaskAction(() -> {
140+
Action activateToolAction = new TaskAction(() -> {
107141
if (Tools.activeTool != tool) {
108142
tool.activate();
109143
}
110144
});
111145

112-
GlobalEvents.registerHotkey(tool.getHotkey(), activateAction);
146+
GlobalEvents.registerHotkey(tool.getHotkey(), activateToolAction);
113147
}
114148

115149
private static void setupSharedHotkey(Tool... sharingTools) {
116-
Action multiToolAction = new AbstractAction() {
150+
Action cycleToolsAction = new AbstractAction() {
117151
@Override
118152
public void actionPerformed(ActionEvent e) {
119153
int activeIndex = -1;
@@ -130,7 +164,8 @@ public void actionPerformed(ActionEvent e) {
130164
sharingTools[nextIndex].activate();
131165
}
132166
};
167+
// all tools in a group are expected to have the same hotkey
133168
char key = sharingTools[0].getHotkey();
134-
GlobalEvents.registerHotkey(key, multiToolAction);
169+
GlobalEvents.registerHotkey(key, cycleToolsAction);
135170
}
136171
}

src/main/java/pixelitor/tools/pen/NodeTool.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ protected void toolDeactivated(View view) {
7777
super.toolDeactivated(view);
7878

7979
lastActive = null;
80+
if (view == null) {
81+
return;
82+
}
8083
view.repaint(); // hide the path
8184
}
8285

src/main/java/pixelitor/tools/pen/PathTool.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ public void initSettingsPanel(ResourceBundle resources) {
5858
"Delete the path");
5959
}
6060

61-
@Override
62-
public boolean hasSharedHotkey() {
63-
return true;
64-
}
65-
6661
/**
6762
* Removes a path from the composition, without adding a history edit.
6863
*/

src/main/java/pixelitor/tools/pen/PenTool.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ protected void toolActivated(View view) {
132132
@Override
133133
protected void toolDeactivated(View view) {
134134
super.toolDeactivated(view);
135+
if (view == null) {
136+
return;
137+
}
135138

136139
Path path = view.getComp().getActivePath();
137140
if (path != null) {

0 commit comments

Comments
 (0)