Skip to content

macOS: hide-if-single has no effect — Tab mode always reserves ISLAND_HEIGHT and keeps its click routing #1915

Description

@shaunwen

Summary

On macOS, [navigation] hide-if-single never takes effect. With a single tab, Rio still offsets content by ISLAND_HEIGHT, leaving a permanent empty band at the top of the window.

The branch that implements it is compiled out on macOS — frontends/rioterm/src/renderer/utils.rs, padding_top_from_config:

if navigation.is_enabled() {          // is_enabled() == (mode == Tab)
    // On Linux/Windows, if hide_if_single is true and there's only one tab,
    // the island is hidden so render from 0 + configured margin
    #[cfg(not(target_os = "macos"))]
    if navigation.hide_if_single && num_tabs <= 1 {
        return constants::PADDING_Y + padding_y_top;
    }

    use crate::renderer::island::ISLAND_HEIGHT;
    return ISLAND_HEIGHT + padding_y_top;   // always taken on macOS
}

The default value makes this more confusing, because it advertises the opposite:

pub fn default_hide_if_single() -> bool {
    cfg!(target_os = "macos")   // defaults to TRUE on macOS, where it does nothing
}

Why it matters

This is most visible when running a terminal multiplexer (herdr, tmux) inside Rio: the multiplexer supplies the tabs, so Rio only ever has one, and the reserved band is pure waste at the top of every window. hide-if-single is exactly the setting that should address it, and it silently does nothing on this platform.

The two available workarounds both fail:

1. Negative top margin. margin = [-28, 10, 10, 10] cancels the band visually — the value confirms the reserved height is exactly ISLAND_HEIGHT — but the island keeps the input layer for those pixels, so anything drawn there stops responding to the mouse. In my case the multiplexer's own tab row lands in that band and can no longer be clicked. This is the hazard described in the comment in rio-backend/src/config/navigation.rs:

Whether the rio-rendered tab strip ("island") is actually painted this frame. Mirrors the gate at island.rs:358 — input layers (click routing, cursor override) must agree with the renderer so the empty band over a hidden island doesn't intercept events.

With hide_if_single compiled out on macOS, the renderer and the input layer agree with each other but both disagree with the user's configuration.

2. mode = "NativeTab". This reserves nothing (0, or ADDITIONAL_PADDING_Y_ON_UNIFIED_TITLEBAR = 2px with macos-use-unified-titlebar = true), so with two or more tabs the macOS tab bar paints over the first rows of terminal content. I tried this with decorations = "Disabled" and with decorations = "Transparent"; both overlap.

So on macOS today there is no configuration that is both flush with one tab and correct with several.

Expected behavior

On macOS, with hide-if-single = true and a single tab, Tab mode should reserve no top band and should not intercept mouse events in that region — matching the documented behaviour and the Linux/Windows implementation. When a second tab appears, the island should be reserved and routed as usual.

Reproduction

  1. ~/.config/rio/config.toml:
margin = [0, 10, 10, 10]

[window]
decorations = "Disabled"

[navigation]
mode = "Tab"
hide-if-single = true
  1. Open Rio with a single tab. An empty band (ISLAND_HEIGHT, meas

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions