Add floating close button to split panes with subtle design
- Added HoverCloseButton class with hover effects - Close button appears in top-right corner of each split pane - Subtle dark semi-transparent design that brightens on hover - Container view approach for proper layout and positioning - Updated help and keyboard shortcuts documentation - Removed non-working Cmd+Shift+W from help displays
This commit is contained in:
Binary file not shown.
@@ -1,5 +1,44 @@
|
||||
import Cocoa
|
||||
|
||||
// MARK: - Hover Close Button
|
||||
class HoverCloseButton: NSButton {
|
||||
override func mouseEntered(with event: NSEvent) {
|
||||
super.mouseEntered(with: event)
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
context.duration = 0.15
|
||||
self.layer?.backgroundColor = NSColor(white: 0.3, alpha: 0.85).cgColor
|
||||
self.contentTintColor = NSColor.white
|
||||
}
|
||||
}
|
||||
|
||||
override func mouseExited(with event: NSEvent) {
|
||||
super.mouseExited(with: event)
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
context.duration = 0.15
|
||||
self.layer?.backgroundColor = NSColor(white: 0.2, alpha: 0.6).cgColor
|
||||
self.contentTintColor = NSColor(white: 0.8, alpha: 0.9)
|
||||
}
|
||||
}
|
||||
|
||||
override func updateTrackingAreas() {
|
||||
super.updateTrackingAreas()
|
||||
|
||||
// Remove old tracking areas
|
||||
for trackingArea in trackingAreas {
|
||||
removeTrackingArea(trackingArea)
|
||||
}
|
||||
|
||||
// Add new tracking area
|
||||
let trackingArea = NSTrackingArea(
|
||||
rect: bounds,
|
||||
options: [.mouseEnteredAndExited, .activeAlways, .inVisibleRect],
|
||||
owner: self,
|
||||
userInfo: nil
|
||||
)
|
||||
addTrackingArea(trackingArea)
|
||||
}
|
||||
}
|
||||
|
||||
class MainWindowController: NSWindowController {
|
||||
var tabManager = TabManager()
|
||||
var tabBar: NSStackView!
|
||||
@@ -511,9 +550,8 @@ class MainWindowController: NSWindowController {
|
||||
File:
|
||||
Cmd+T - New Tab
|
||||
Cmd+W - Close Tab
|
||||
Cmd+D - Split Pane Vertically
|
||||
Cmd+Shift+D - Split Pane Horizontally
|
||||
Cmd+Shift+W - Close Pane
|
||||
Cmd+D - Split Pane Vertically (side-by-side)
|
||||
Cmd+Shift+D - Split Pane Horizontally (top-bottom)
|
||||
Cmd+K - Clear Screen
|
||||
Cmd+Shift+K - Clear Scrollback
|
||||
|
||||
@@ -538,7 +576,9 @@ class MainWindowController: NSWindowController {
|
||||
|
||||
Terminal:
|
||||
Tab - Accept Auto-suggestion
|
||||
Up/Down - Navigate Command History
|
||||
Double-click Tab - Rename Tab
|
||||
Click × button - Close split pane
|
||||
"""
|
||||
alert.alertStyle = .informational
|
||||
alert.addButton(withTitle: "OK")
|
||||
@@ -563,10 +603,10 @@ class MainWindowController: NSWindowController {
|
||||
splitView?.dividerStyle = .thin
|
||||
splitView?.autoresizingMask = [.width, .height]
|
||||
|
||||
// Move current session to split view
|
||||
// Move current session to split view with close button
|
||||
currentSession.scrollView.removeFromSuperview()
|
||||
addCloseButtonToPane(currentSession.scrollView, index: 0)
|
||||
splitView?.addArrangedSubview(currentSession.scrollView)
|
||||
let containerView = addCloseButtonToPane(currentSession.scrollView, index: 0)
|
||||
splitView?.addArrangedSubview(containerView)
|
||||
panes = [currentSession]
|
||||
}
|
||||
|
||||
@@ -575,8 +615,8 @@ class MainWindowController: NSWindowController {
|
||||
newSession.setupTextView(frame: CGRect(x: 0, y: 0, width: contentView.bounds.width / 2, height: contentView.bounds.height))
|
||||
newSession.startShell()
|
||||
|
||||
addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
||||
splitView?.addArrangedSubview(newSession.scrollView)
|
||||
let newContainerView = addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
||||
splitView?.addArrangedSubview(newContainerView)
|
||||
panes.append(newSession)
|
||||
activePaneIndex = panes.count - 1
|
||||
|
||||
@@ -607,10 +647,10 @@ class MainWindowController: NSWindowController {
|
||||
splitView?.dividerStyle = .thin
|
||||
splitView?.autoresizingMask = [.width, .height]
|
||||
|
||||
// Move current session to split view
|
||||
// Move current session to split view with close button
|
||||
currentSession.scrollView.removeFromSuperview()
|
||||
addCloseButtonToPane(currentSession.scrollView, index: 0)
|
||||
splitView?.addArrangedSubview(currentSession.scrollView)
|
||||
let containerView = addCloseButtonToPane(currentSession.scrollView, index: 0)
|
||||
splitView?.addArrangedSubview(containerView)
|
||||
panes = [currentSession]
|
||||
}
|
||||
|
||||
@@ -619,8 +659,8 @@ class MainWindowController: NSWindowController {
|
||||
newSession.setupTextView(frame: CGRect(x: 0, y: 0, width: contentView.bounds.width, height: contentView.bounds.height / 2))
|
||||
newSession.startShell()
|
||||
|
||||
addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
||||
splitView?.addArrangedSubview(newSession.scrollView)
|
||||
let newContainerView = addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
||||
splitView?.addArrangedSubview(newContainerView)
|
||||
panes.append(newSession)
|
||||
activePaneIndex = panes.count - 1
|
||||
|
||||
@@ -633,29 +673,39 @@ class MainWindowController: NSWindowController {
|
||||
newSession.textView.window?.makeFirstResponder(newSession.textView)
|
||||
}
|
||||
|
||||
private func addCloseButtonToPane(_ scrollView: NSScrollView, index: Int) {
|
||||
// Remove any existing close button
|
||||
scrollView.subviews.filter { $0.tag == 9999 }.forEach { $0.removeFromSuperview() }
|
||||
private func addCloseButtonToPane(_ scrollView: NSScrollView, index: Int) -> NSView {
|
||||
// Create a container view that will hold both the scroll view and the close button
|
||||
let containerView = NSView(frame: scrollView.frame)
|
||||
containerView.autoresizingMask = [.width, .height]
|
||||
containerView.wantsLayer = true
|
||||
|
||||
// Create close button
|
||||
let closeButton = NSButton(frame: NSRect(x: scrollView.bounds.width - 30, y: scrollView.bounds.height - 30, width: 24, height: 24))
|
||||
// Adjust scroll view frame to fill container
|
||||
scrollView.frame = containerView.bounds
|
||||
scrollView.autoresizingMask = [.width, .height]
|
||||
containerView.addSubview(scrollView)
|
||||
|
||||
// Create close button with subtle styling
|
||||
let closeButton = HoverCloseButton(frame: NSRect(x: containerView.bounds.width - 35, y: containerView.bounds.height - 35, width: 24, height: 24))
|
||||
closeButton.title = "×"
|
||||
closeButton.bezelStyle = .circular
|
||||
closeButton.font = NSFont.systemFont(ofSize: 16, weight: .bold)
|
||||
closeButton.tag = 9999 // Special tag to identify close buttons
|
||||
closeButton.font = NSFont.systemFont(ofSize: 16, weight: .medium)
|
||||
closeButton.tag = 9999
|
||||
closeButton.target = self
|
||||
closeButton.action = #selector(closePaneFromButton(_:))
|
||||
closeButton.toolTip = "Close pane (Cmd+Shift+W)"
|
||||
closeButton.autoresizingMask = [.minXMargin, .minYMargin]
|
||||
closeButton.wantsLayer = true
|
||||
closeButton.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.5).cgColor
|
||||
closeButton.layer?.backgroundColor = NSColor(white: 0.2, alpha: 0.6).cgColor
|
||||
closeButton.layer?.cornerRadius = 12
|
||||
closeButton.contentTintColor = .white
|
||||
closeButton.contentTintColor = NSColor(white: 0.8, alpha: 0.9)
|
||||
|
||||
// Store the pane index
|
||||
objc_setAssociatedObject(closeButton, "paneIndex", index, .OBJC_ASSOCIATION_RETAIN)
|
||||
|
||||
scrollView.addSubview(closeButton)
|
||||
// Add button on top of scroll view
|
||||
containerView.addSubview(closeButton)
|
||||
|
||||
return containerView
|
||||
}
|
||||
|
||||
@objc func closePaneFromButton(_ sender: NSButton) {
|
||||
|
||||
@@ -568,6 +568,8 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
||||
Keyboard Shortcuts:
|
||||
• Cmd+T - New tab
|
||||
• Cmd+W - Close tab
|
||||
• Cmd+D - Split pane vertically (side-by-side)
|
||||
• Cmd+Shift+D - Split pane horizontally (top-bottom)
|
||||
• Cmd+R - Search command history (fuzzy search)
|
||||
• Cmd+K - AI command generator
|
||||
• Cmd+P - Command snippets
|
||||
|
||||
Reference in New Issue
Block a user