Add Total Commander-style locked tabs (lock, protect from closing, restore at locked folder)#3804
Open
dzyla wants to merge 9 commits into
Open
Add Total Commander-style locked tabs (lock, protect from closing, restore at locked folder)#3804dzyla wants to merge 9 commits into
dzyla wants to merge 9 commits into
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A locked tab previously only refused to close and reopened at its locked folder on restart; nothing returned it there at runtime, so a locked tab that had been navigated away stayed away. Replace the pinned boolean with NemoTabLockMode: NONE navigate freely, keep the last folder (default) RETURN navigate freely, return to the locked folder when reselected NEW_TAB never navigate, divert folder changes into a new unlocked tab Both locked modes keep the existing close protection. Enforcement sits in nemo_window_slot_open_location_full(), which every navigation call site routes through, plus nemo_window_back_or_forward(), which reaches begin_location_change() directly when flags are 0. The RETURN snap-back hangs off ::switch-page, deferred to an idle so it does not navigate while the notebook is still switching. Search is exempt from the NEW_TAB diversion: sync_search_location_cb() asserts the originating slot holds a search directory, so sending the search to another tab would abort. The tab context menu grows an "Open Changes in New Tab" check item, sensitive only while the tab is locked. Saved sessions change from a(sb) to a(si), each entry being (uri, lock mode). All tabs are saved; locked ones save their locked folder. Sessions written by the earlier a(sb) build are dropped once on type mismatch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The snap-back for NEMO_TAB_LOCK_RETURN hung off the notebook's ::switch-page, which only fires when a notebook's current page changes. A locked tab can stop being the one in use without that happening: switching to the other pane in split view, or being the only tab in its pane. In those cases the tab never went home, so a tab locked at ~ and navigated to ~/Downloads still showed Downloads on return. Move the hook to the slot's own "active" handler. nemo_window_set_active_slot() emits it for every way a slot becomes current, covering tab switches and pane switches alike. Hooking "inactive" instead would park the tab the moment it is left, but navigating a background slot makes its fresh view take focus - the icon and list views call nemo_window_slot_make_hosting_pane_active() from their focus-in handlers - which drags the user back to the tab they just left. Activation-side navigation touches only the slot already in focus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Commit 901f12b changed saved-tabs-left/right from "as" to "a(sb)", and 6ed2207 changed them again to "a(si)". Both retyped a key that already ships in master, and GSettings answers a type mismatch by discarding the stored value and handing back the default. Every tab a user had saved went silently missing on upgrade -- twice. The plan justified this as safe because the keys were "internal and unreleased", which was wrong on the second count: they landed upstream in PR linuxmint#3661. Leave saved-tabs-left/right at "as" and carry the lock modes in new position-aligned "ai" keys, saved-tab-locks-left/right. A lock list may be shorter than its tab list -- anything it does not cover is unlocked -- so tabs written by a Nemo without tab locking restore unchanged, and a pane with nothing locked writes [] exactly as before. Trailing unlocked entries are trimmed on save to keep that the common case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Session state was only written from real_window_close and the Quit action, so logging out with a window open saved nothing: the session manager sends SIGTERM, and with no handler installed the process died where it stood. Tabs only ever persisted if you closed the window by hand first, which defeats the point of restoring them. Handle SIGTERM and SIGHUP, saving through the same path Quit uses. Flush with g_settings_sync() once the state is written, since both callers are on their way out and an unflushed GSettings write dies with the process. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
notebook_switch_page_cb credited the slot's "inactive" handler for sending a locked tab home, but d1a39ce deliberately moved that to the "active" handler; the comment said the opposite of what the code does. build_tab_label still called locked tabs "pinned", the name 6ed2207 replaced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Total Commander-style locked tabs, building on the tab session restore merged in #3661.
What it does
changes-prevent-symbolic) in its tab label and its close button is hidden.org.nemo.preferences restore-tabs-on-startupenabled, lock state persists across restarts: a locked tab is restored at its locked folder (not the last visited directory) and comes back locked; unlocked tabs keep the existing behaviour of restoring at their last location.What it deliberately does not do
uri_is_native_session_uri, unchanged from Restore Last Window Tabs on Startup #3661. A lock on such a tab works within the session but is not persisted.Implementation
src/nemo-window-slot.{h,c}—NemoWindowSlotgainsNemoTabLockMode lock_mode(NONE/RETURN/NEW_TAB) andchar *locked_uri, withnemo_window_slot_set_lock_mode (slot, mode, locked_uri)plus getters. Passinglocked_uri == NULLcaptures the slot's pending or current location (menu toggle path); passing a URI locks explicitly (session-restore path). The setter triggers a tab-label resync;locked_uriis freed in dispose. ARETURNtab is sent back to its locked folder from the slot'sactivehandler, on an idle, so the snap-back does not race the in-flight::switch-page; doing it on activation rather than deactivation avoids navigating a background slot, whose fresh view would steal focus back.src/nemo-notebook.{h,c}—build_tab_label ()packs a hidden padlockGtkImageinto the tab-label hbox. Newnemo_notebook_sync_lock ()toggles padlock and close-button visibility, and sets a tooltip describing the active lock mode.src/nemo-window-pane.c— the tab context menu gains a separator, the Loc_k Tab check item, and the Open Changes in New _Tab check item (sensitive only while locked). Check states are set before the signals are connected, so building the menu doesn't toggle anything. The Close Tab item is desensitized for locked tabs.notebook_tab_close_requested ()— the funnel for the tab close button, middle-click, and the popup Close item — returns early for locked tabs.src/nemo-window-menus.c—action_close_window_slot_callback ()(Ctrl+W / File > Close) returns early for a locked active tab.src/nemo-window-manage-views.c—nemo_window_slot_open_location_full ()diverts aNEW_TABslot's navigation into a fresh tab.nemo_window_back_or_forward ()routes such a slot throughopen_locationso a history step becomes a new tab rather than moving the locked one.src/nemo-window.c+libnemo-private/org.nemo.gschema.xml—saved-tabs-left/saved-tabs-rightkeep their existingastype. Lock modes travel in two new position-alignedaikeys,saved-tab-locks-left/saved-tab-locks-right. A lock list may be shorter than its tab list — anything it does not cover is an unlocked tab — so a session written by a Nemo without tab locking restores unchanged, and a pane with nothing locked writes[]exactly as today. Trailing unlocked entries are trimmed on save to keep that the common case.Retyping the existing keys was tried first and is a bad idea: GSettings answers a type mismatch by discarding the stored value and returning the default, so changing
saved-tabs-*fromasto a tuple array silently throws away the saved tabs of everyone upgrading past #3661. The parallel-key layout avoids that, and is forward- and backward-compatible in both directions.src/nemo-application.c— session state was only written fromreal_window_close ()and the Quit action, so logging out with a window open saved nothing: the session manager sendsSIGTERMand, with no handler installed, the process died where it stood.SIGTERMandSIGHUPnow save through the same path Quit uses, followed byg_settings_sync (), since both callers are on their way out and an unflushed GSettings write dies with the process.Testing
No unit harness covers window code, so this was verified behaviourally on a Mint system with packages built from this branch, with
restore-tabs-on-startupenabled.Interactive:
nemo <path>still bypasses restore.Scripted against the built binary, each run in a private session bus and dconf database (
dbus-run-session+XDG_CONFIG_HOME), usingtimeout --signal=TERMto stand in for a logout:Schema compiles with
glib-compile-schemas --strict; build is warning-clean in the touched files.🤖 Generated with Claude Code