Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "Recently Used Boards" Menu #10816

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 71 additions & 1 deletion app/src/processing/app/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ public class Base {
private List<JMenu> boardsCustomMenus;
private List<JMenuItem> programmerMenus;

// these variables help rebuild the "recently used boards"
// menu on board selection
private HashMap<String, JMenuItem> recentBoardItems;
private List<JMenuItem> recentBoardsToClear = new LinkedList<>();;
private JMenu boardMenu;
private int recentBoardsJMenuIndex;

private PdeKeywords pdeKeywords;
private final List<JMenuItem> recentSketchesMenuItems = new LinkedList<>();

Expand Down Expand Up @@ -1360,6 +1367,26 @@ public void onBoardOrPortChange() {
}
}

// Update recent boards list in preferences
List<String> newRecentBoardIds = new ArrayList<String>();
String currentBoard = PreferencesData.get("board");
for (String recentBoard : PreferencesData.getCollection("recent.boards")){
if (!recentBoard.equals(currentBoard)) {
newRecentBoardIds.add(recentBoard);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this just use Collection.remove to remove currentBoard rather than manually looping? Might need to make a copy of the collection first, though just modifying the existing collection might be even shorter.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe this would be a good usecase for this new java streaming collection API, where you could do something like "get the current list, filter out the selected board, prepend the current board, and limit to n elements". Not sure about exact syntax, but I think this could very well end up concise and readable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into that. I'm actually not that familiar with Java, this was my first crack at it. I suspect there are a lot of optimizations to do with collections since I mostly treated them like arrays.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't figure out the optimization for this without breaking it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented this in my branch: matthijskooijman@818c3fe

newRecentBoardIds.add(0, currentBoard);
if (newRecentBoardIds.size() == 6) {
newRecentBoardIds.remove(5);
}
NPoole marked this conversation as resolved.
Show resolved Hide resolved
PreferencesData.setCollection("recent.boards", newRecentBoardIds);
try {
rebuildRecentBoardsList();
} catch (Exception e) {
//TODO show error
e.printStackTrace();
}

// Update editors status bar
for (Editor editor : editors) {
editor.onBoardOrPortChange();
Expand Down Expand Up @@ -1433,9 +1460,10 @@ protected void onIndexesUpdated() throws Exception {

public void rebuildBoardsMenu() throws Exception {
boardsCustomMenus = new LinkedList<>();
recentBoardItems = new HashMap<String, JMenuItem>();

// The first custom menu is the "Board" selection submenu
JMenu boardMenu = new JMenu(tr("Board"));
boardMenu = new JMenu(tr("Board"));
boardMenu.putClientProperty("removeOnWindowDeactivation", true);
MenuScroller.setScrollerFor(boardMenu).setTopFixedCount(1);

Expand All @@ -1458,6 +1486,16 @@ public void actionPerformed(ActionEvent actionevent) {
}));
boardsCustomMenus.add(boardMenu);

// Insert recently used boards menu and remember index for insertion later
if (PreferencesData.has("recent.boards")) {
NPoole marked this conversation as resolved.
Show resolved Hide resolved
// Insert menu label
boardMenu.add(new JSeparator());
JMenuItem label = new JMenuItem(tr("Recently Used Boards"));
NPoole marked this conversation as resolved.
Show resolved Hide resolved
label.setEnabled(false);
boardMenu.add(label);
recentBoardsJMenuIndex = boardMenu.getItemCount();
}

// If there are no platforms installed we are done
if (BaseNoGui.packages.size() == 0)
return;
Expand Down Expand Up @@ -1555,6 +1593,23 @@ private String getPlatformUniqueId(TargetPlatform platform) {
return platform.getId() + "_" + platform.getFolder();
}

// clear the previous menu items from the "recently used boards"
// menu and repopulate with updated items
private void rebuildRecentBoardsList() throws Exception {
Collection<String> recentBoardIds = new LinkedList<>();
recentBoardIds = PreferencesData.getCollection("recent.boards");
NPoole marked this conversation as resolved.
Show resolved Hide resolved
int idxAdv = 0;
for (JMenuItem itemToClear : recentBoardsToClear) {
boardMenu.remove(itemToClear);
}
recentBoardsToClear.clear();
for (String boardId : recentBoardIds) {
boardMenu.add(recentBoardItems.get(boardId), recentBoardsJMenuIndex+idxAdv);
recentBoardsToClear.add(recentBoardItems.get(boardId));
idxAdv++;
}
}

private JRadioButtonMenuItem createBoardMenusAndCustomMenus(
final List<JMenu> boardsCustomMenus, List<JMenuItem> menuItemsToClickAfterStartup,
Map<String, ButtonGroup> buttonGroupsMap,
Expand Down Expand Up @@ -1585,6 +1640,21 @@ public void actionPerformed(ActionEvent actionevent) {

JRadioButtonMenuItem item = new JRadioButtonMenuItem(action);

// create an action for the "recent boards" copy of this menu item
// which clicks the original menu item
Action actionClone = new AbstractAction(board.getName()) {
public void actionPerformed(ActionEvent actionevent) {
item.setSelected(true);
item.getAction().actionPerformed(new ActionEvent(this, -1, ""));
}
};

// create a menu item for the "recent boards" menu
JMenuItem itemClone = new JMenuItem(actionClone);
NPoole marked this conversation as resolved.
Show resolved Hide resolved

// populate list of menuitem copies
recentBoardItems.put(boardId, itemClone);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an optimization, maybe you could put item in recentBoardItems (which probably needs to be renamed, then) and then create the clone action and item in rebuildRecentBoardsList so you only need to create 5 of them, rather than tens or hundreds.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I have is that during createBoardMenusAndCustomMenus() I don't know what is going to be on the recent boards list, and the list is updated more often than createBoardMenusAndCustomMenus() is called. Whether I save the item, a copy of the item, or just the action, I need to save one for every board that's installed. There are only two solutions I can imagine:

  1. Add a function that drills down through all installed packages and calls createBoardMenusAndCustomMenus(). This new function would probably be called in rebuildRecentBoardsList(). I think this would significantly slow down switching boards or ports so probably not a good idea.

  2. Instead of cloning and saving the menu items, retrieve them from the menu during rebuildRecentBoardsList(). Presumably, they live somewhere? I spent a long time trying to figure out how to do this... never did.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether I save the item, a copy of the item, or just the action, I need to save one for every board that's installed.

Yes, I was suggesting you still have a map that contains an item for each board in the entire menu, but you wouldn't have to create the clone item and action for each, just for the ones you're actually going to use.

You could probably also find them without the map, but just using the map is probably a lot faster then trying to find them from the menus.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented this locally, cleans up things nicely. However, some other small improvements made broke things, so I haven't pushed this anywhere yet, no time to fix that now, so I'll come back to this. Also the custom board menu stuff might also need some further changes here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented this in my branch matthijskooijman@a9631da

Note that when stuff is refactored by saving fqbn's, as I suggest in #10887, this might end up different again, though.


if (selBoard.equals(boardId) && selPackage.equals(packageName)
&& selPlatform.equals(platformName)) {
menuItemsToClickAfterStartup.add(item);
Expand Down