Skip to content

feat(window): add fill-center title bar policy and BasicTitleBar APIs#245

Open
RavenLiao wants to merge 5 commits into
NucleusFramework:mainfrom
RavenLiao:fillTitleBar
Open

feat(window): add fill-center title bar policy and BasicTitleBar APIs#245
RavenLiao wants to merge 5 commits into
NucleusFramework:mainfrom
RavenLiao:fillTitleBar

Conversation

@RavenLiao
Copy link
Copy Markdown
Contributor

🚀 Description

This PR introduces a new title bar layout customization layer with a built-in fill-center policy and adds dedicated basic title bar APIs for advanced layout scenarios.

Key updates include:

  • Added TitleBarLayoutPolicy with Default (legacy-compatible) and FillCenter behavior.
  • Added BasicTitleBar(...) and BasicDialogTitleBar(...) as low-level entry points.
  • Kept existing TitleBar(...) / DialogTitleBar(...) APIs compatible by delegating to Basic* + Default.
  • Wired layoutPolicy through core/JBR/JNI title bar paths.
  • Added Fill Title demo window in example for behavior validation.
  • Updated demo tab interactions in title bar to use titleBarClickable, improving macOS fullscreen interaction stability.

📄 Motivation and Context

Current title bar center content behavior is primarily wrap-content oriented, which makes "center fills remaining space" scenarios (e.g. tab strips) hard to express consistently.

This change provides a framework-native fill-center layout model while preserving existing behavior and API compatibility:

  • No breaking changes for existing title bar usage.
  • Advanced users can opt into FillCenter via Basic* APIs.
  • Platform integration details remain internal; only content layout policy is exposed.

🧪 How Has This Been Tested?

Build/test checks:

  • ./gradlew.bat :decorated-window-core:compileKotlin :decorated-window-jbr:compileKotlin :decorated-window-jni:compileKotlin :example:compileKotlin
    Result: BUILD SUCCESSFUL

Manual validation:

  • Verified Fill Title demo behavior in windowed mode and fullscreen mode.
  • Confirmed title bar controls and content still render/behave correctly with new policy plumbing.
  • Verified macOS fullscreen demo tab switching no longer relies on plain clickable in title bar hit-test-sensitive area.

📦 Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

✅ Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

@kdroidFilter
Copy link
Copy Markdown
Collaborator

kdroidFilter commented May 15, 2026

Thank you very much for your work, I'm thinking of accepting the PR, although long-term I wonder if using a title bar is really a good pattern. It's originally a fork of Jewel, but honestly I think we'd just need a modifier that indicates a given composable should allow the window to be dragged, and simply overlay the native buttons in the right position — that would allow for unified UIs with a sidebar and not necessarily a title bar. This would also resolve issue #129 and would perhaps make things much simpler. I don't think I'd deprecate the title bar, but in my opinion it's just an old inherited pattern

@RavenLiao
Copy link
Copy Markdown
Contributor Author

Thank you very much for your work, I'm thinking of accepting the PR, although long-term I wonder if using a title bar is really a good pattern. It's originally a fork of Jewel, but honestly I think we'd just need a modifier that indicates a given composable should allow the window to be dragged, and simply overlay the native buttons in the right position — that would allow for unified UIs with a sidebar and not necessarily a title bar. This would also resolve issue #129 and would perhaps make things much simpler. I don't think I'd deprecate the title bar, but in my opinion it's just an old inherited pattern

Yes, something like this?

data class WindowControlsArea(
    val side: WindowControlsSide, // Start (macOS) or End (Windows)
    val width: Dp,                // horizontal space the buttons occupy
    val height: Dp,               // how far down they extend
    val offsetY: Dp = 0.dp,       // shifts during fullscreen menu-bar animation
)

val LocalWindowControlsArea = staticCompositionLocalOf { WindowControlsArea(...) }

// ---

DecoratedWindow(...) {
    val controls = LocalWindowControlsArea.current

    Box(Modifier.fillMaxSize()) {
        HeroImage(Modifier.fillMaxSize())

        Sidebar(
            Modifier
                .windowDraggableArea()
                .windowControlsPadding() // reads LocalWindowControlsArea internally
        )
    }
}

windowControlsPadding() is just a convenience on top of LocalWindowControlsArea; code that needs precise avoidance (e.g. clipping behind the traffic lights) can read the area directly.

This also feels like the natural resolution to #129 — content extends freely, each element opts in to avoidance rather than being forced into a fixed TitleBar layout.

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