Skip to content

Patched Add dual-panelayout options (vertical spit, separate sidebars/nav bars/status menu)#3735

Closed
cdmdotnet wants to merge 5 commits intolinuxmint:masterfrom
cdmdotnet:master
Closed

Patched Add dual-panelayout options (vertical spit, separate sidebars/nav bars/status menu)#3735
cdmdotnet wants to merge 5 commits intolinuxmint:masterfrom
cdmdotnet:master

Conversation

@cdmdotnet
Copy link

Includes the missing check-in the previous PR lacked - confirmed the github build action now runs.
There's a spec doc, the most useful aspect of which will be the wire-frame diagrams that give you an idea of what the changes are/do.
nemo_dual_pane_spec.docx

Dual-pane enhancements:

vertical split mode (panes stacked one above other),
separate sidebar per pane,
separate Path Bar / Location Entry per pane
separate status bar per pane

All options default to off, preserving existing behaviour when not enabled. New preferences exposed in a dedicated Dual Pane section of the Behaviour tab.

Details

  • "Show panes vertically – one above the other" (default: off)
    Adds a VPaned orientation for split-view in addition to the existing
    side-by-side HPaned layout. The split_view_hpane is recreated with
    the appropriate GtkOrientation when the preference changes.
  • "Separate sidebar per pane" (default: off, requires vertical split)
    In vertical split mode each pane column gets its own independent
    sidebar (places or tree) locked to that pane's navigation so
    browsing in one pane does not move the other pane's sidebar.
    Implemented by wrapping pane1 in a new inline HPaned
    (primary_pane_content_paned) and adding a second sidebar box
    (sidebar2 / secondary_pane_content_paned) for pane2.
  • "Separate Path Bar/Location Entry per pane" (default: off, requires vertical split)
    Embeds a full toolbar (path bar / location entry) directly inside
    each pane column so each pane tracks its own navigation history
    independently without sharing the window-level toolbar.
  • "Separate statusbar per pane" (default: off, requires vertical split + separate sidebar)
    Adds a full-width NemoStatusBar below each pane column (spanning
    both the sidebar and content area). The global statusbar is hidden
    by concealing its outer event-box wrapper rather than changing its
    visible property, preserving the GSettings binding that controls the
    floating-bar "selected items" overlay. Each per-pane bar is locked
    to its pane via a new PROP_PANE construct property so its zoom
    slider and sidebar buttons operate on that pane only. The places /
    tree toggle buttons call the new nemo_window_set_pane_sidebar_type()
    helper which swaps the sidebar widget in-place without firing the
    global notify::sidebar-view-id signal, keeping each pane's sidebar
    type independent.

…idebars, separate nav bars)

Three new preferences in Preferences > Behavior > Dual Pane:

- "Show panes vertically (one above the other)" [dual-pane-vertical-split]
  Switches the split-view HPaned to a VPaned so panes stack top/bottom
  instead of side by side.

- "Separate sidebar per pane" [dual-pane-separate-sidebar]
  Requires vertical split. Each pane gets its own Places/Tree sidebar,
  allowing the second pane's sidebar to track its own location without
  needing to change focus first. Sidebar type (Places/Tree) and
  visibility remain global toggles.

- "Separate path bar / location entry per pane" [dual-pane-separate-nav-bar]
  Requires vertical split. Each pane embeds its own toolbar (path bar
  and location entry) above its tab strip. Toggle Location Entry,
  clicking the path bar to switch to location entry, and Go menu /
  bookmarks all operate on the focused pane only.

"Always start in dual-pane view" is moved into the new Dual Pane section.

Implementation:
- GSettings schema: 3 new bool keys in org.nemo.preferences (default false)
- Layout: pri_paned/sec_paned wrapper HPaneds hold sidebar+pane columns
  when separate-sidebar is active; torn down cleanly on pref change or
  split-view-off
- Sidebar: NemoPlacesSidebar and FMTreeView accept an optional
  NemoWindowPane* to filter loading-uri signals to their own pane
- Toolbar: nemo_window_pane_embed/detach_toolbar reparents the toolbar
  into/out of the pane widget for per-pane nav bar mode
- CSS: .nemo-active-sidebar / .nemo-inactive-pane classes colour each
  sidebar to match its pane's active/inactive state
- Path bar callbacks: right-click, middle-click, and location-entry
  toggle all route to pane->active_slot (not the window's active slot)
- Destroy order: pane1 wrapper → sidebar2 → sidebar → panes (all paths)

Tests:
- test/test-dual-pane-schema.c: 9 GSettings unit tests verifying the
  three new keys exist with correct schema defaults and round-trip
  correctly; runs without a display or D-Bus session
… + separate sidebar)

      Adds a full-width NemoStatusBar below each pane column (spanning
      both the sidebar and content area).  Each per-pane bar is locked
      to its pane so its zoom and sidebar buttons operate on that pane only.
      The places / tree toggle buttons swap the per-pane sidebar widget,
      keeping each pane's sidebar type independent.
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);

properties[PROP_PANE] = g_param_spec_pointer ("locked-pane",
Copy link
Member

Choose a reason for hiding this comment

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

should be a GObject property (like PROP_WINDOW)

Comment on lines +598 to +615
/* Restore per-pane layout if applicable */
{
gboolean split = nemo_window_split_view_showing (window);
gboolean vertical = g_settings_get_boolean (nemo_preferences,
NEMO_PREFERENCES_DUAL_PANE_VERTICAL_SPLIT);
gboolean sep_sidebar = g_settings_get_boolean (nemo_preferences,
NEMO_PREFERENCES_DUAL_PANE_SEPARATE_SIDEBAR);
gboolean sep_statusbar = g_settings_get_boolean (nemo_preferences,
NEMO_PREFERENCES_DUAL_PANE_SEPARATE_STATUSBAR);
/* pane1 wrapper only when separate_sidebar ON (independent of sep_nav) */
if (split && vertical && sep_sidebar && window->details->show_sidebar) {
nemo_window_set_up_pane1_wrapper (window);
nemo_window_set_up_sidebar2 (window);
if (sep_statusbar) {
nemo_window_set_up_per_pane_statusbars (window);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

duplicated code

Comment on lines +2155 to +2172

/* Respond to dual pane preference changes */
g_signal_connect_swapped (nemo_preferences,
"changed::" NEMO_PREFERENCES_DUAL_PANE_VERTICAL_SPLIT,
G_CALLBACK (dual_pane_prefs_changed),
window);
g_signal_connect_swapped (nemo_preferences,
"changed::" NEMO_PREFERENCES_DUAL_PANE_SEPARATE_SIDEBAR,
G_CALLBACK (dual_pane_prefs_changed),
window);
g_signal_connect_swapped (nemo_preferences,
"changed::" NEMO_PREFERENCES_DUAL_PANE_SEPARATE_NAV_BAR,
G_CALLBACK (dual_pane_prefs_changed),
window);
g_signal_connect_swapped (nemo_preferences,
"changed::" NEMO_PREFERENCES_DUAL_PANE_SEPARATE_STATUSBAR,
G_CALLBACK (dual_pane_prefs_changed),
window);
Copy link
Member

Choose a reason for hiding this comment

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

never disconnected, guaranteed crash if you close the window then change settings later.

Comment on lines +18 to +19
*.sh
*.deb
Copy link
Member

Choose a reason for hiding this comment

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

unnecessary

Comment on lines +184 to +189
if (window->details->statusbar1_eb != NULL) {
GList *ch = gtk_container_get_children (
GTK_CONTAINER (window->details->statusbar1_eb));
if (ch != NULL && NEMO_IS_STATUS_BAR (ch->data)) {
target = GTK_STATUSBAR (
NEMO_STATUS_BAR (ch->data)->real_statusbar);
Copy link
Member

Choose a reason for hiding this comment

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

what is even going on here??

@mtwebster
Copy link
Member

This still remains something we're not interested in. If I enable all options for vertical panes here, it essentially becomes 2 windows. It's simple enough to open a second window and tile them, and we avoid a lot of extra complication this PR brings.

  • If you have multiple windows and close one, then change a setting, nemo crashes (stale signal connections).
  • New file serves no purpose: pkg/usr/share/glib-2.0/schemas/org.nemo.gschema.xml
  • PRs should not bump changelogs or versions (the maintainer does this)
  • If you enable 'vertical' mode and mess with the additional settings, you quickly end up in a buggy state:
image image image

I commented some areas in the code that raise flags, but honestly I gave up - there are a huge number of inconsistencies and questionable choices going on here to just 'make it work'. Less effort put into 'test' scripts and more actual testing would have been invaluable here.

some AI feedback:

  PR #3735 Review Summary: Dual-Pane Layout Options                                                                         
                                                                                                                            
  Critical Issues (3 found)
                                                                                                                            
  1. GSettings signal handlers never disconnected — use-after-free (code-reviewer + error-hunter)                           
  - nemo-window.c: Four g_signal_connect_swapped calls connect dual_pane_prefs_changed to the global nemo_preferences
  singleton, but nemo_window_finalize never disconnects them. After a window is destroyed, any preference change invokes the
   callback with a freed window pointer. Crash guaranteed in multi-window usage.                                            
  - Fix: Add g_signal_handlers_disconnect_by_func(nemo_preferences, dual_pane_prefs_changed, window) to                     
  nemo_window_finalize.                                                                                
                                                                                                                            
  2. Memory leak in places sidebar loading_uri_callback (code-reviewer + error-hunter)
  - nemo-places-sidebar.c:1485: nemo_window_slot_get_current_uri() returns a newly-allocated string passed directly to      
  g_strcmp0 without storing/freeing it. Leaks on every navigation event with separate sidebars enabled. The tree sidebar    
  handles this correctly with g_free(slot_uri).                                                                             
                                                                                                                            
  3. Committed pkg/ directory — 918-line duplicate schema (code-reviewer)
  - pkg/usr/share/glib-2.0/schemas/org.nemo.gschema.xml is a full copy of the source schema that was never in the repo      
  before. This is a packaging artifact that will drift out of sync. Should be removed from the commit (and possibly added to
   .gitignore).                                                                                                             
                                                                                                                            
  Important Issues (8 found)

  4. locked_pane / pane stored as raw pointers — dangling pointer risk (error-hunter + type-analyzer)                       
  - All three _new_for_pane constructors (statusbar, places sidebar, tree sidebar) store a raw NemoWindowPane* with no weak
  reference. If pane destruction order ever changes, this becomes use-after-free. PROP_PANE uses g_param_spec_pointer       
  instead of g_param_spec_object, losing type safety and ref-counting.
                                                                                                                            
  5. Initialization race in _new_for_pane constructors (error-hunter)
  - nemo_places_sidebar_new_for_pane calls set_parent_window (which triggers update_places → sidebar_get_slot) before
  setting sidebar->pane. Both sidebars initially display the wrong pane's location. Fix: set pane before calling            
  set_parent_window.                                                                                            
                                                                                                                            
  6. refresh_sidebar1_pane_lock removes wrong child when separator present (error-hunter)
  - The function removes children->data (first child), but after set_up_pane1_wrapper adds a separator at position 0, the   
  first child is the separator, not the sidebar widget. Results in double sidebar widgets stacked in the box.               
                                                                                                                            
  7. .gitignore adds *.sh and *.deb — too broad (code-reviewer)                                                             
  - *.sh will silently ignore any future shell scripts (one already exists in the repo). These are developer-specific
  entries that belong in ~/.config/git/ignore.                                                                              
  
  8. Inconsistent indentation: 4-space vs tabs (code-reviewer)                                                              
  - All new code uses 4-space indentation in a file whose modeline specifies indent-tabs-mode: t. Creates a jarring mix with
   existing tab-indented code.                                                                                              
                              
  9. sidebar2_id declared but never used (type-analyzer + code-reviewer + error-hunter)                                     
  - gchar *sidebar2_id in NemoWindowDetails is never written, read, or freed. Dead code suggesting incomplete               
  implementation.                                                                                                           
                                                                                                                            
  10. Pane1 statusbar discovered via container introspection instead of direct pointer (type-analyzer + error-hunter)       
  - nemo_window_push_status and nemo_window_sync_zoom_widgets find pane1's statusbar via                                    
  gtk_container_get_children(statusbar1_eb). Meanwhile pane2's is stored directly in nemo_status_bar2. This asymmetry is    
  fragile — a direct nemo_status_bar1 pointer would be safer and consistent.                                                
                                                                                                                            
  11. DRY violation: "restore per-pane layout" block duplicated (code-reviewer)
  - Nearly identical 17-line blocks in nemo_window_show_sidebar and side_pane_id_changed. Should be extracted to a helper.  
                                                                                                                            
  Suggestions (6 found)                                                                                                     
                                                                                                                            
  - Add debug logging to guard clauses in setup/teardown functions for diagnosability                                       
  - Use g_param_spec_object for PROP_PANE instead of g_param_spec_pointer                                                   
  - Add a toolbar_embedded boolean to NemoWindowPane instead of repeated parent-pointer checks                              
  - Consider a centralized nemo_window_tear_down_per_pane_layout() wrapper to enforce correct teardown ordering             
  - Compare slot/pane pointers directly in loading_uri_callback instead of fragile URI string comparison                    
  - Add NULL guard on window->details->panes before accessing panes->data in several functions                              
            

@mtwebster mtwebster closed this Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants