Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Welcome window toolbar growing and captioning its search field after choosing Icon and Text.

## [0.76.0] - 2026-09-28

Agent mode: one AI session that works across the whole connection window, with every statement it ran.
Expand Down
35 changes: 27 additions & 8 deletions TablePro/Views/Welcome/WelcomeWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@ import AppKit
import SwiftUI
import TableProConnectionLibrary

@MainActor
private final class WelcomeWindow: NSWindow {
override var toolbar: NSToolbar? {
didSet {
guard let toolbar else { return }
Self.fixAtIconOnly(toolbar)
}
}

private static func fixAtIconOnly(_ toolbar: NSToolbar) {
toolbar.displayMode = .iconOnly
toolbar.allowsUserCustomization = false
toolbar.autosavesConfiguration = false
if #available(macOS 15.0, *) {
toolbar.allowsDisplayModeCustomization = false
}
}
}

@MainActor
internal final class WelcomeWindowController: NSWindowController, NSWindowDelegate {
internal static let contentSize = NSSize(width: 900, height: 600)
Expand All @@ -30,9 +49,8 @@ internal final class WelcomeWindowController: NSWindowController, NSWindowDelega
controller.viewModel.focusList()
}

private init(viewModel: WelcomeViewModel) {
self.viewModel = viewModel
let window = NSWindow(
internal static func makeWelcomeWindow() -> NSWindow {
let window = WelcomeWindow(
contentRect: NSRect(origin: .zero, size: Self.contentSize),
styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView],
backing: .buffered,
Expand All @@ -46,13 +64,14 @@ internal final class WelcomeWindowController: NSWindowController, NSWindowDelega
window.isRestorable = false
window.tabbingMode = .disallowed
window.collectionBehavior.insert([.fullScreenNone, .fullScreenDisallowsTiling])

let toolbar = NSToolbar(identifier: "com.TablePro.welcome.toolbar")
toolbar.displayMode = .iconOnly
toolbar.allowsUserCustomization = false
window.toolbar = toolbar
window.toolbar = NSToolbar(identifier: "com.TablePro.welcome.toolbar")
window.toolbarStyle = .unified
return window
}

private init(viewModel: WelcomeViewModel) {
self.viewModel = viewModel
let window = Self.makeWelcomeWindow()
window.contentViewController = WelcomeSplitViewController(viewModel: viewModel)
window.contentMinSize = Self.contentSize
window.contentMaxSize = Self.contentSize
Expand Down
100 changes: 100 additions & 0 deletions TableProTests/Views/Welcome/WelcomeToolbarTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// WelcomeToolbarTests.swift
// TableProTests
//

import AppKit
import SwiftUI
import Testing

@testable import TablePro

@MainActor
struct WelcomeToolbarTests {
private func withWelcomeWindow(_ body: (NSWindow) async throws -> Void) async rethrows {
let window = WelcomeWindowController.makeWelcomeWindow()
#expect(!window.isReleasedWhenClosed)
defer {
window.contentViewController = nil
window.close()
}
try await body(window)
}

private func expectFixedAtIconOnly(_ toolbar: NSToolbar?) {
#expect(toolbar?.displayMode == .iconOnly)
#expect(toolbar?.allowsUserCustomization == false)
#expect(toolbar?.autosavesConfiguration == false)
if #available(macOS 15.0, *) {
#expect(toolbar?.allowsDisplayModeCustomization == false)
}
}

@Test("The welcome window's own toolbar offers no display mode choice")
func ownToolbarIsFixedAtIconOnly() async {
await withWelcomeWindow { window in
#expect(window.toolbar != nil)
#expect(window.toolbarStyle == .unified)
expectFixedAtIconOnly(window.toolbar)
}
}

@Test("A toolbar installed after the window is built is fixed at Icon Only too")
func laterToolbarIsFixedAtIconOnly() async {
await withWelcomeWindow { window in
let toolbar = NSToolbar(identifier: "com.TablePro.tests.welcome.replacement")
toolbar.displayMode = .iconAndLabel
toolbar.allowsUserCustomization = true
toolbar.autosavesConfiguration = true
if #available(macOS 15.0, *) {
toolbar.allowsDisplayModeCustomization = true
}

window.toolbar = toolbar

expectFixedAtIconOnly(window.toolbar)
}
}

@Test("The toolbar SwiftUI installs for the list pane offers no display mode choice")
func bridgedToolbarIsFixedAtIconOnly() async throws {
guard #available(macOS 14.0, *) else { return }
try await withWelcomeWindow { window in
let seed = try #require(window.toolbar)
let content = NSHostingController(rootView: BridgedToolbarContent())
content.sizingOptions = []
content.sceneBridgingOptions = [.toolbars]

window.contentViewController = content
var waits = 0
while window.toolbar === seed, waits < 100 {
waits += 1
try await Task.sleep(for: .milliseconds(10))
}

let bridged = try #require(window.toolbar)
#expect(bridged !== seed)
#expect(bridged.items.contains { $0 is NSSearchToolbarItem })
expectFixedAtIconOnly(bridged)
}
}
}

private struct BridgedToolbarContent: View {
@State private var searchText = ""

var body: some View {
Color.clear
.searchable(text: $searchText, placement: .toolbar)
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
Button {} label: {
Label("New Connection", systemImage: "plus")
}
Button {} label: {
Label("New Group", systemImage: "folder.badge.plus")
}
}
}
}
}
Loading