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
|
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 {
|
class MainWindowController: NSWindowController {
|
||||||
var tabManager = TabManager()
|
var tabManager = TabManager()
|
||||||
var tabBar: NSStackView!
|
var tabBar: NSStackView!
|
||||||
@@ -511,9 +550,8 @@ class MainWindowController: NSWindowController {
|
|||||||
File:
|
File:
|
||||||
Cmd+T - New Tab
|
Cmd+T - New Tab
|
||||||
Cmd+W - Close Tab
|
Cmd+W - Close Tab
|
||||||
Cmd+D - Split Pane Vertically
|
Cmd+D - Split Pane Vertically (side-by-side)
|
||||||
Cmd+Shift+D - Split Pane Horizontally
|
Cmd+Shift+D - Split Pane Horizontally (top-bottom)
|
||||||
Cmd+Shift+W - Close Pane
|
|
||||||
Cmd+K - Clear Screen
|
Cmd+K - Clear Screen
|
||||||
Cmd+Shift+K - Clear Scrollback
|
Cmd+Shift+K - Clear Scrollback
|
||||||
|
|
||||||
@@ -538,7 +576,9 @@ class MainWindowController: NSWindowController {
|
|||||||
|
|
||||||
Terminal:
|
Terminal:
|
||||||
Tab - Accept Auto-suggestion
|
Tab - Accept Auto-suggestion
|
||||||
|
Up/Down - Navigate Command History
|
||||||
Double-click Tab - Rename Tab
|
Double-click Tab - Rename Tab
|
||||||
|
Click × button - Close split pane
|
||||||
"""
|
"""
|
||||||
alert.alertStyle = .informational
|
alert.alertStyle = .informational
|
||||||
alert.addButton(withTitle: "OK")
|
alert.addButton(withTitle: "OK")
|
||||||
@@ -563,10 +603,10 @@ class MainWindowController: NSWindowController {
|
|||||||
splitView?.dividerStyle = .thin
|
splitView?.dividerStyle = .thin
|
||||||
splitView?.autoresizingMask = [.width, .height]
|
splitView?.autoresizingMask = [.width, .height]
|
||||||
|
|
||||||
// Move current session to split view
|
// Move current session to split view with close button
|
||||||
currentSession.scrollView.removeFromSuperview()
|
currentSession.scrollView.removeFromSuperview()
|
||||||
addCloseButtonToPane(currentSession.scrollView, index: 0)
|
let containerView = addCloseButtonToPane(currentSession.scrollView, index: 0)
|
||||||
splitView?.addArrangedSubview(currentSession.scrollView)
|
splitView?.addArrangedSubview(containerView)
|
||||||
panes = [currentSession]
|
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.setupTextView(frame: CGRect(x: 0, y: 0, width: contentView.bounds.width / 2, height: contentView.bounds.height))
|
||||||
newSession.startShell()
|
newSession.startShell()
|
||||||
|
|
||||||
addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
let newContainerView = addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
||||||
splitView?.addArrangedSubview(newSession.scrollView)
|
splitView?.addArrangedSubview(newContainerView)
|
||||||
panes.append(newSession)
|
panes.append(newSession)
|
||||||
activePaneIndex = panes.count - 1
|
activePaneIndex = panes.count - 1
|
||||||
|
|
||||||
@@ -607,10 +647,10 @@ class MainWindowController: NSWindowController {
|
|||||||
splitView?.dividerStyle = .thin
|
splitView?.dividerStyle = .thin
|
||||||
splitView?.autoresizingMask = [.width, .height]
|
splitView?.autoresizingMask = [.width, .height]
|
||||||
|
|
||||||
// Move current session to split view
|
// Move current session to split view with close button
|
||||||
currentSession.scrollView.removeFromSuperview()
|
currentSession.scrollView.removeFromSuperview()
|
||||||
addCloseButtonToPane(currentSession.scrollView, index: 0)
|
let containerView = addCloseButtonToPane(currentSession.scrollView, index: 0)
|
||||||
splitView?.addArrangedSubview(currentSession.scrollView)
|
splitView?.addArrangedSubview(containerView)
|
||||||
panes = [currentSession]
|
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.setupTextView(frame: CGRect(x: 0, y: 0, width: contentView.bounds.width, height: contentView.bounds.height / 2))
|
||||||
newSession.startShell()
|
newSession.startShell()
|
||||||
|
|
||||||
addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
let newContainerView = addCloseButtonToPane(newSession.scrollView, index: panes.count)
|
||||||
splitView?.addArrangedSubview(newSession.scrollView)
|
splitView?.addArrangedSubview(newContainerView)
|
||||||
panes.append(newSession)
|
panes.append(newSession)
|
||||||
activePaneIndex = panes.count - 1
|
activePaneIndex = panes.count - 1
|
||||||
|
|
||||||
@@ -633,29 +673,39 @@ class MainWindowController: NSWindowController {
|
|||||||
newSession.textView.window?.makeFirstResponder(newSession.textView)
|
newSession.textView.window?.makeFirstResponder(newSession.textView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addCloseButtonToPane(_ scrollView: NSScrollView, index: Int) {
|
private func addCloseButtonToPane(_ scrollView: NSScrollView, index: Int) -> NSView {
|
||||||
// Remove any existing close button
|
// Create a container view that will hold both the scroll view and the close button
|
||||||
scrollView.subviews.filter { $0.tag == 9999 }.forEach { $0.removeFromSuperview() }
|
let containerView = NSView(frame: scrollView.frame)
|
||||||
|
containerView.autoresizingMask = [.width, .height]
|
||||||
|
containerView.wantsLayer = true
|
||||||
|
|
||||||
// Create close button
|
// Adjust scroll view frame to fill container
|
||||||
let closeButton = NSButton(frame: NSRect(x: scrollView.bounds.width - 30, y: scrollView.bounds.height - 30, width: 24, height: 24))
|
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.title = "×"
|
||||||
closeButton.bezelStyle = .circular
|
closeButton.bezelStyle = .circular
|
||||||
closeButton.font = NSFont.systemFont(ofSize: 16, weight: .bold)
|
closeButton.font = NSFont.systemFont(ofSize: 16, weight: .medium)
|
||||||
closeButton.tag = 9999 // Special tag to identify close buttons
|
closeButton.tag = 9999
|
||||||
closeButton.target = self
|
closeButton.target = self
|
||||||
closeButton.action = #selector(closePaneFromButton(_:))
|
closeButton.action = #selector(closePaneFromButton(_:))
|
||||||
closeButton.toolTip = "Close pane (Cmd+Shift+W)"
|
closeButton.toolTip = "Close pane (Cmd+Shift+W)"
|
||||||
closeButton.autoresizingMask = [.minXMargin, .minYMargin]
|
closeButton.autoresizingMask = [.minXMargin, .minYMargin]
|
||||||
closeButton.wantsLayer = true
|
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.layer?.cornerRadius = 12
|
||||||
closeButton.contentTintColor = .white
|
closeButton.contentTintColor = NSColor(white: 0.8, alpha: 0.9)
|
||||||
|
|
||||||
// Store the pane index
|
// Store the pane index
|
||||||
objc_setAssociatedObject(closeButton, "paneIndex", index, .OBJC_ASSOCIATION_RETAIN)
|
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) {
|
@objc func closePaneFromButton(_ sender: NSButton) {
|
||||||
|
|||||||
@@ -568,6 +568,8 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
|||||||
Keyboard Shortcuts:
|
Keyboard Shortcuts:
|
||||||
• Cmd+T - New tab
|
• Cmd+T - New tab
|
||||||
• Cmd+W - Close 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+R - Search command history (fuzzy search)
|
||||||
• Cmd+K - AI command generator
|
• Cmd+K - AI command generator
|
||||||
• Cmd+P - Command snippets
|
• Cmd+P - Command snippets
|
||||||
|
|||||||
Reference in New Issue
Block a user