mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
HACK: ctrl-tab to swap to last active tab
KVO on tabGroup.selectedWindow works but I can't figure out how to detect when the tabGroup changes, so the observer gets attached to the wrong tabGroup TODO: lastActiveTabIndex needs to be per-tab-group, not global, but I can't figure out how to store per-tab-group or per-associated-window state
This commit is contained in:
@ -142,6 +142,7 @@ typedef enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_TAB_PREVIOUS = -1,
|
GHOSTTY_TAB_PREVIOUS = -1,
|
||||||
GHOSTTY_TAB_NEXT = -2,
|
GHOSTTY_TAB_NEXT = -2,
|
||||||
|
GHOSTTY_TAB_LAST_ACTIVE = -3,
|
||||||
} ghostty_tab_e;
|
} ghostty_tab_e;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -36,6 +36,7 @@ class AppDelegate: NSObject,
|
|||||||
@IBOutlet private var menuPaste: NSMenuItem?
|
@IBOutlet private var menuPaste: NSMenuItem?
|
||||||
@IBOutlet private var menuSelectAll: NSMenuItem?
|
@IBOutlet private var menuSelectAll: NSMenuItem?
|
||||||
|
|
||||||
|
@IBOutlet private var menuLastActiveTab: NSMenuItem?
|
||||||
@IBOutlet private var menuToggleFullScreen: NSMenuItem?
|
@IBOutlet private var menuToggleFullScreen: NSMenuItem?
|
||||||
@IBOutlet private var menuZoomSplit: NSMenuItem?
|
@IBOutlet private var menuZoomSplit: NSMenuItem?
|
||||||
@IBOutlet private var menuPreviousSplit: NSMenuItem?
|
@IBOutlet private var menuPreviousSplit: NSMenuItem?
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22689"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@ -22,6 +22,7 @@
|
|||||||
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
||||||
<outlet property="menuEqualizeSplits" destination="3gH-VD-vL9" id="SiZ-ce-FOF"/>
|
<outlet property="menuEqualizeSplits" destination="3gH-VD-vL9" id="SiZ-ce-FOF"/>
|
||||||
<outlet property="menuIncreaseFontSize" destination="CIH-ey-Z6x" id="hkc-9C-80E"/>
|
<outlet property="menuIncreaseFontSize" destination="CIH-ey-Z6x" id="hkc-9C-80E"/>
|
||||||
|
<outlet property="menuLastActiveTab" destination="ucq-9T-8lf" id="KbP-Y4-YBa"/>
|
||||||
<outlet property="menuMoveSplitDividerDown" destination="Zj7-2W-fdF" id="997-LL-nlN"/>
|
<outlet property="menuMoveSplitDividerDown" destination="Zj7-2W-fdF" id="997-LL-nlN"/>
|
||||||
<outlet property="menuMoveSplitDividerLeft" destination="wSR-ny-j1a" id="HCZ-CI-2ob"/>
|
<outlet property="menuMoveSplitDividerLeft" destination="wSR-ny-j1a" id="HCZ-CI-2ob"/>
|
||||||
<outlet property="menuMoveSplitDividerRight" destination="CcX-ql-QU4" id="rIn-PK-fVM"/>
|
<outlet property="menuMoveSplitDividerRight" destination="CcX-ql-QU4" id="rIn-PK-fVM"/>
|
||||||
@ -41,8 +42,8 @@
|
|||||||
<outlet property="menuSelectSplitLeft" destination="cTK-oy-KuV" id="Jpr-5q-dqz"/>
|
<outlet property="menuSelectSplitLeft" destination="cTK-oy-KuV" id="Jpr-5q-dqz"/>
|
||||||
<outlet property="menuSelectSplitRight" destination="upj-mc-L7X" id="nLY-o1-lky"/>
|
<outlet property="menuSelectSplitRight" destination="upj-mc-L7X" id="nLY-o1-lky"/>
|
||||||
<outlet property="menuServices" destination="aQe-vS-j8Q" id="uWQ-Wo-T1L"/>
|
<outlet property="menuServices" destination="aQe-vS-j8Q" id="uWQ-Wo-T1L"/>
|
||||||
<outlet property="menuSplitRight" destination="VUR-Ld-nLx" id="RxO-Zw-ovb"/>
|
|
||||||
<outlet property="menuSplitDown" destination="UDZ-4y-6xL" id="fgZ-Wb-8OR"/>
|
<outlet property="menuSplitDown" destination="UDZ-4y-6xL" id="fgZ-Wb-8OR"/>
|
||||||
|
<outlet property="menuSplitRight" destination="VUR-Ld-nLx" id="RxO-Zw-ovb"/>
|
||||||
<outlet property="menuTerminalInspector" destination="QwP-M5-fvh" id="wJi-Dh-S9f"/>
|
<outlet property="menuTerminalInspector" destination="QwP-M5-fvh" id="wJi-Dh-S9f"/>
|
||||||
<outlet property="menuToggleFullScreen" destination="8kY-Pi-KaY" id="yQg-6V-OO6"/>
|
<outlet property="menuToggleFullScreen" destination="8kY-Pi-KaY" id="yQg-6V-OO6"/>
|
||||||
<outlet property="menuZoomSplit" destination="oPd-mn-IEH" id="wTu-jK-egI"/>
|
<outlet property="menuZoomSplit" destination="oPd-mn-IEH" id="wTu-jK-egI"/>
|
||||||
@ -233,7 +234,17 @@
|
|||||||
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
|
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
<menuItem isSeparatorItem="YES" id="Bws-Hg-Q2a"/>
|
||||||
|
<menuItem title="Show Next Tab" id="ucq-9T-8lf">
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
CQ
|
||||||
|
</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" control="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="selectLastActiveTab:" target="-1" id="nJL-fj-u7w"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="CMt-XK-G4G"/>
|
||||||
<menuItem title="Toggle Full Screen" keyEquivalent="f" id="8kY-Pi-KaY">
|
<menuItem title="Toggle Full Screen" keyEquivalent="f" id="8kY-Pi-KaY">
|
||||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
@ -517,6 +517,17 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
splitMoveFocus(direction: .right)
|
splitMoveFocus(direction: .right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func selectLastActiveTab(_ sender: Any) {
|
||||||
|
guard let surface = focusedSurface else { return }
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Ghostty.Notification.ghosttyGotoTab,
|
||||||
|
object: surface,
|
||||||
|
userInfo: [
|
||||||
|
Ghostty.Notification.GotoTabKey: GHOSTTY_TAB_LAST_ACTIVE.rawValue,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func equalizeSplits(_ sender: Any) {
|
@IBAction func equalizeSplits(_ sender: Any) {
|
||||||
guard let surface = focusedSurface?.surface else { return }
|
guard let surface = focusedSurface?.surface else { return }
|
||||||
ghostty.splitEqualize(surface: surface)
|
ghostty.splitEqualize(surface: surface)
|
||||||
@ -677,6 +688,8 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
} else {
|
} else {
|
||||||
finalIndex = selectedIndex + 1
|
finalIndex = selectedIndex + 1
|
||||||
}
|
}
|
||||||
|
} else if (tabIndex == GHOSTTY_TAB_LAST_ACTIVE.rawValue) {
|
||||||
|
finalIndex = ghostty.lastActiveTabIndex
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,13 @@ class TerminalWindow: NSWindow {
|
|||||||
resetZoomTabButton.contentTintColor = .secondaryLabelColor
|
resetZoomTabButton.contentTintColor = .secondaryLabelColor
|
||||||
resetZoomToolbarButton.contentTintColor = .tertiaryLabelColor
|
resetZoomToolbarButton.contentTintColor = .tertiaryLabelColor
|
||||||
tab.attributedTitle = attributedTitle
|
tab.attributedTitle = attributedTitle
|
||||||
|
|
||||||
|
// if resigned key and not selected tab, then we were the last active tab
|
||||||
|
if let tabGroup,
|
||||||
|
tabGroup.selectedWindow != self,
|
||||||
|
let selfIndex = tabGroup.windows.firstIndex(of: self) {
|
||||||
|
(windowController as? TerminalController)?.ghostty.lastActiveTabIndex = selfIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layoutIfNeeded() {
|
override func layoutIfNeeded() {
|
||||||
|
@ -24,6 +24,9 @@ extension Ghostty {
|
|||||||
/// Optional delegate
|
/// Optional delegate
|
||||||
weak var delegate: GhosttyAppDelegate?
|
weak var delegate: GhosttyAppDelegate?
|
||||||
|
|
||||||
|
// TODO: this needs to be per-tab-group
|
||||||
|
var lastActiveTabIndex = 0
|
||||||
|
|
||||||
/// The readiness value of the state.
|
/// The readiness value of the state.
|
||||||
@Published var readiness: Readiness = .loading
|
@Published var readiness: Readiness = .loading
|
||||||
|
|
||||||
|
@ -3265,6 +3265,20 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
} else log.warn("runtime doesn't implement gotoPreviousTab", .{});
|
} else log.warn("runtime doesn't implement gotoPreviousTab", .{});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.last_active_tab => {
|
||||||
|
log.warn("last_active_tab is deprecated, use gotoLastActiveTab", .{});
|
||||||
|
if (@hasDecl(apprt.Surface, "hasTabs")) {
|
||||||
|
if (!self.rt_surface.hasTabs()) {
|
||||||
|
log.debug("surface has no tabs, ignoring last_active_tab binding", .{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@hasDecl(apprt.Surface, "gotoLastActiveTab")) {
|
||||||
|
self.rt_surface.gotoLastActiveTab();
|
||||||
|
} else log.warn("runtime doesn't implement gotoLastActiveTab", .{});
|
||||||
|
},
|
||||||
|
|
||||||
.next_tab => {
|
.next_tab => {
|
||||||
if (@hasDecl(apprt.Surface, "hasTabs")) {
|
if (@hasDecl(apprt.Surface, "hasTabs")) {
|
||||||
if (!self.rt_surface.hasTabs()) {
|
if (!self.rt_surface.hasTabs()) {
|
||||||
|
@ -133,6 +133,7 @@ pub const App = struct {
|
|||||||
const GotoTab = enum(i32) {
|
const GotoTab = enum(i32) {
|
||||||
previous = -1,
|
previous = -1,
|
||||||
next = -2,
|
next = -2,
|
||||||
|
last_active = -3,
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -983,6 +984,15 @@ pub const Surface = struct {
|
|||||||
func(self.userdata, .previous);
|
func(self.userdata, .previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gotoLastActiveTab(self: *Surface) void {
|
||||||
|
const func = self.app.opts.goto_tab orelse {
|
||||||
|
log.info("runtime embedder does not goto_tab", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
func(self.userdata, .last_active);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gotoNextTab(self: *Surface) void {
|
pub fn gotoNextTab(self: *Surface) void {
|
||||||
const func = self.app.opts.goto_tab orelse {
|
const func = self.app.opts.goto_tab orelse {
|
||||||
log.info("runtime embedder does not goto_tab", .{});
|
log.info("runtime embedder does not goto_tab", .{});
|
||||||
|
@ -1516,6 +1516,11 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
|
|||||||
.{ .key = .{ .translated = .right_bracket }, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .{ .translated = .right_bracket }, .mods = .{ .super = true, .shift = true } },
|
||||||
.{ .next_tab = {} },
|
.{ .next_tab = {} },
|
||||||
);
|
);
|
||||||
|
try result.keybind.set.put(
|
||||||
|
alloc,
|
||||||
|
.{ .key = .{ .translated = .tab }, .mods = .{ .ctrl = true } },
|
||||||
|
.{ .last_active_tab = {} },
|
||||||
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .{ .translated = .d }, .mods = .{ .super = true } },
|
.{ .key = .{ .translated = .d }, .mods = .{ .super = true } },
|
||||||
|
@ -215,6 +215,9 @@ pub const Action = union(enum) {
|
|||||||
/// Go to the next tab.
|
/// Go to the next tab.
|
||||||
next_tab: void,
|
next_tab: void,
|
||||||
|
|
||||||
|
/// Go to the last active tab.
|
||||||
|
last_active_tab: void,
|
||||||
|
|
||||||
/// Go to the tab with the specific number, 1-indexed.
|
/// Go to the tab with the specific number, 1-indexed.
|
||||||
goto_tab: usize,
|
goto_tab: usize,
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user