Skip to content

Conversation

@subhramit
Copy link
Member

@subhramit subhramit commented Sep 12, 2025

Closes #6352

This brings back an "improved" (per-tab) version of chronological navigation across entries that existed in JabRef 4.x.

image

Steps to test

  1. Start JabRef
  2. Open Chocolate.bib
  3. Click (or double-click to open the entry editor) on an entry
  4. Click on another entry (the "Back" navigation button should now be available)
  5. Repeat step 4 a couple of times
  6. Press the "Back" navigation button any number of times till valid (the "Forward" navigation button should now be available)
  7. Press the "Forward" navigation button any number of times as valid
  8. Play with permutations of step 6 and 7 and check if a) the order is correct b) the entry editor correctly reflects the entry that is navigated to.
  9. Open another non-empty library and check steps 3-8, switching between libraries (individual navigation states and history of each should persist)
  10. Test the same functionality with the keybindings (alt+left, alt+right)
  11. Open the Welcome tab (ensure that the navigation buttons are unavailable)
  12. Close all libraries/tabs (ensure that the navigation buttons are unavailable)

Mandatory checks

@subhramit subhramit added this to the 6.0-beta2🎄❄️🎅 milestone Sep 12, 2025
@subhramit subhramit added the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Sep 12, 2025
@subhramit subhramit requested a review from ThiloteE September 12, 2025 16:34

private void newEntryShowing(BibEntry entry) {
// skip history updates if this is from a back/forward operation
if (backOrForwardInProgress) {
Copy link
Member

Choose a reason for hiding this comment

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

Is this condition necessary? I would use such a check if scrolling and entry showing run on different threads, and use AtomicBoolean instead. But here both would run on the JavaFX thread, so it is not possible for this method to be called while scrolling is in progress.

Copy link
Member Author

@subhramit subhramit Sep 12, 2025

Choose a reason for hiding this comment

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

This is to differentiate if the call is coming from back/forward navigation button (in which case history management is nit needed, just need to update the UI) or if the user clicked a new entry (in which case history list needs an update).

In other words, without this, a back navigation call would be treated as direct navigation on the maintable and nextEntries.clear() would immediately undo the work the back() method just did and break the forward button.

Copy link
Member Author

Choose a reason for hiding this comment

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

I changed the name of the boolean to backOrForwardNavigation to avoid confusion

Comment on lines 122 to 123
private final List<BibEntry> previousEntries = new ArrayList<>();
private final List<BibEntry> nextEntries = new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

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

We can replace this by one list and an index that points to the currently selected entry:

  • When an entry is selected we add it to the list.
  • When back called we decrement (if possible) the index and navigate to that entry
  • When forward called we increment (if possible) and navigate.

Copy link
Member Author

@subhramit subhramit Sep 12, 2025

Choose a reason for hiding this comment

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

I thought about this a bit, I am a bit reluctant about this scenario:
You seelct entry A, then B, then C, then D.

You click Back twice. You are now viewing B.

From B, you click on a new entry, E.

The old "forward" history (entries C, D) must be discarded. The new history should be A -> B -> E.

The logic to truncate the "forward" part of the list will be slightly less cleaner (clearing the forward "sublist" of the original list and adding the new entry) than the way we just do nextEntries.clear() now, but if two people are in favor of this I'll shift to a single list.

Copy link
Member

Choose a reason for hiding this comment

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

I thought whether Eclipse Collections and sublists could help here. However, the current code is very clean and I would keep it.


image

Copy link
Member

Choose a reason for hiding this comment

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

IMO, managing one state is much easier them managing 2+, because you need to keep them in sync: Whenever you modify one you need to think which one needs to also change.

But, you can still make the current design better by encapsulating the forward and backward list into a record.

Copy link
Member Author

Choose a reason for hiding this comment

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

Record is suitable in a context of immutability. If we wish to encapsulate, can do with a separate class

Copy link
Member Author

Choose a reason for hiding this comment

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

Refactored in commit d890eea, lets see if you guys like this

Comment on lines 506 to 507
// when no entries are selected, don't update navigation history
// but still update the navigation state
Copy link
Member

Choose a reason for hiding this comment

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

This comment does not add anything that the code doesn't already show, a better comment would describe the why.

Copy link
Member Author

Choose a reason for hiding this comment

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

I just checked, it is not possible to "de-select" an entry by clicking on an empty cell in the main table.
I am refining the comments and keeping this as a fallback just in case there is a way to do that.
If there is not, maybe this can be removed,

@ThiloteE
Copy link
Member

ThiloteE commented Sep 12, 2025

Don't show the arrows in the GUI, if only one library is open. They take a lot of screenspace. Especially on small screens, if I remember right, there was a bug that would cause JabRef to crash when there are too many symbols in that row. That was one of the reasons that we removed social buttons from that row (+ the fact that social buttons for Linkedin or Twitter are really just a distraction in the GUI).

@subhramit
Copy link
Member Author

subhramit commented Sep 12, 2025

Don't show the arrows in the GUI, if only one library is open.

Do you mean when no library is open? As this will make the user rely only on keyboard shortcuts?

Also, the undo/redo buttons also have the same behavior.

@subhramit
Copy link
Member Author

subhramit commented Sep 12, 2025

Don't show the arrows in the GUI, if only one library is open.

Do you mean when no library is open? As this will make the user rely only on keyboard shortcuts?

Also, the undo/redo buttons also have the same behavior.

Also, on shrinking, one by one all the buttons shift to a side menu:

image

@InAnYan
Copy link
Member

InAnYan commented Sep 12, 2025

That's interesting

@ThiloteE
Copy link
Member

Don't show the arrows in the GUI, if only one library is open.

Do you mean when no library is open? As this will make the user rely only on keyboard shortcuts?

Also, the undo/redo buttons also have the same behavior.

Aren't those buttons to switch between libraries? Or what is it for? If there is only one library, they would be useless, right?

@subhramit
Copy link
Member Author

subhramit commented Sep 12, 2025

Don't show the arrows in the GUI, if only one library is open.

Do you mean when no library is open? As this will make the user rely only on keyboard shortcuts?
Also, the undo/redo buttons also have the same behavior.

Aren't those buttons to switch between libraries? Or what is it for? If there is only one library, they would be useless, right?

No, it's for history navigation amongst entries of a library (like the back/forward button in a browser). Check the linked issue once, it'll be clear.

private final BooleanProperty nonUndoableChangeProperty = new SimpleBooleanProperty(false);
private final List<BibEntry> previousEntries = new ArrayList<>();
private final List<BibEntry> nextEntries = new ArrayList<>();
private BibEntry currentlyShowing;
Copy link
Member

Choose a reason for hiding this comment

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

This misses a connection to org.jabref.gui.maintable.MainTable#clearAndSelect(org.jabref.model.entry.BibEntry).

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't get this - there is BibEntry toShow in navigateToEntry, on which this class' clearAndSelect is called, which is indeed MainTable#clearAndSelect internally. I don't think we can use it for currentlyShowing?

Copy link
Member

Choose a reason for hiding this comment

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

My quick thoughts were: If there is an entry selected, I push the back button and then the forward button, the entry should be selected - and not an "arbitrary" one.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh got it. Yeah, no arbitrary entries are selected.

Copy link
Member

Choose a reason for hiding this comment

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

I hope, its just a binding thing 😅

Comment on lines +16 to +17
private final List<BibEntry> previousEntries = new ArrayList<>();
private final List<BibEntry> nextEntries = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

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

Using plain ArrayList instead of JavaFX ObservableList prevents UI from automatically updating when the collection changes. This impacts reactive UI updates.

Comment on lines +34 to +36
if (currentEntry != null) {
previousEntries.add(currentEntry);
}
Copy link

Choose a reason for hiding this comment

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

Null check for currentEntry indicates possible null state handling. Methods should use Optional instead of null for better type safety and clarity.

return;
}

// a new selection invalidates the forward history
Copy link

Choose a reason for hiding this comment

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

Comment merely restates what the code does (clearing nextEntries). It doesn't provide additional information about why this is necessary.

@koppor koppor added this pull request to the merge queue Sep 13, 2025
Merged via the queue into JabRef:main with commit e7a5420 Sep 13, 2025
40 checks passed
@koppor koppor deleted the chronological-navigation branch September 13, 2025 15:24
Siedlerchr added a commit that referenced this pull request Sep 13, 2025
* upstream/main:
  Chronological navigation in libraries (#13863)
  Adapt code style (#13874)
  LSP Refactor to use ranges instead of string matching (#13862)
  Use jspecify (#13873)
  changed Keyword.of to accept List<String> instead of varargs (#13871)
@InAnYan
Copy link
Member

InAnYan commented Sep 25, 2025

If I have selected several entries, will this work?

Use case (please allow me to use will after if in English):

I'm selecting many entries from a big library to filter out irrelevant research. I want to focus only on selecting entries. 
If I'll make a mistake and I'll click on some entry without CTRL, my selection and all my work will go to trash and I would have to reselect entries from the very beginning.

I find this immensively useful

@subhramit
Copy link
Member Author

subhramit commented Sep 26, 2025

If I have selected several entries, will this work?

Use case (please allow me to use will after if in English):

I'm selecting many entries from a big library to filter out irrelevant research. I want to focus only on selecting entries. 
If I'll make a mistake and I'll click on some entry without CTRL, my selection and all my work will go to trash and I would have to reselect entries from the very beginning.

I find this immensively useful

This is a very good idea. While developing, I did not think about multi-select behaviour. Can you file an issue and mark it good third?
I'll look into it if nobody tries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: maintable project: jabcon status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Navigate between entries (Back/Forward button)

5 participants