diff --git a/metainfo.xml b/metainfo.xml index d3246cac9a..50b03ada8c 100644 --- a/metainfo.xml +++ b/metainfo.xml @@ -113,6 +113,7 @@
  • Adds `MoveTabTo` action to move tabs to a specific position (#1695)
  • Adds handling of control codes for Ctrl+5|6|7|8 (#1701)
  • Adds CenterCursor (`zz`) vi motion
  • +
  • Adds ability to name tabs (#1690)
  • diff --git a/src/contour/Config.h b/src/contour/Config.h index 7447c5e867..36272ba6f5 100644 --- a/src/contour/Config.h +++ b/src/contour/Config.h @@ -180,11 +180,10 @@ struct MouseConfig struct IndicatorConfig { std::string left { " {InputMode:Bold,Color=#FFFF00}" - "{Tabs:ActiveColor=#FFFF00,Left= │ }" "{SearchPrompt:Left= │ }" "{TraceMode:Bold,Color=#FFFF00,Left= │ }" "{ProtectedMode:Bold,Left= │ }" }; - std::string middle { "{Title:Left= « ,Right= » }" }; + std::string middle { "{Tabs: ActiveColor=#FFFF00}" }; std::string right { "{HistoryLineCount:Faint,Color=#c0c0c0} │ {Clock:Bold}" }; }; diff --git a/src/contour/TerminalSession.cpp b/src/contour/TerminalSession.cpp index b491048608..0f92dc60ea 100644 --- a/src/contour/TerminalSession.cpp +++ b/src/contour/TerminalSession.cpp @@ -149,6 +149,43 @@ namespace settings.indicatorStatusLine.left = profile.statusLine.value().indicator.left; settings.indicatorStatusLine.middle = profile.statusLine.value().indicator.middle; settings.indicatorStatusLine.right = profile.statusLine.value().indicator.right; + settings.tabNamingMode = [&]() { + // try to find Tab section in one of the status line segments + + std::string segment; + if (profile.statusLine.value().indicator.left.find("Tabs") != std::string::npos) + { + segment = profile.statusLine.value().indicator.left; + } + else if (profile.statusLine.value().indicator.middle.find("Tabs") != std::string::npos) + { + segment = profile.statusLine.value().indicator.middle; + } + else if (profile.statusLine.value().indicator.right.find("Tabs") != std::string::npos) + { + segment = profile.statusLine.value().indicator.right; + } + + // check if indexing is defined + if (segment.find("Indexing=") != std::string::npos) + { + // cut the string after indexing= + std::string indexing = segment.substr(segment.find("Indexing=") + 9); + // cut right part of the string + indexing = indexing.substr(0, indexing.find(',')); + indexing = indexing.substr(0, indexing.find('}')); + + std::ranges::transform( + indexing, indexing.begin(), [](unsigned char c) { return std::tolower(c); }); + + if (indexing == "title") + { + return vtbackend::TabsNamingMode::Title; + } + } + return vtbackend::TabsNamingMode::Indexing; + }(); + settings.syncWindowTitleWithHostWritableStatusDisplay = profile.statusLine.value().syncWindowTitleWithHostWritableStatusDisplay; if (auto const* p = preferredColorPalette(profile.colors.value(), colorPreference)) @@ -262,6 +299,7 @@ void TerminalSession::attachDisplay(display::TerminalDisplay& newDisplay) void TerminalSession::scheduleRedraw() { _terminal.markScreenDirty(); + _manager->Update(); if (_display) _display->scheduleRedraw(); } diff --git a/src/contour/TerminalSession.h b/src/contour/TerminalSession.h index ddc808a5fd..2338072f17 100644 --- a/src/contour/TerminalSession.h +++ b/src/contour/TerminalSession.h @@ -220,6 +220,14 @@ class TerminalSession: public QAbstractItemModel, public vtbackend::Terminal::Ev ~TerminalSession() override; int id() const noexcept { return _id; } + std::optional name() const noexcept + { + if (terminal().tabName()) + return terminal().tabName(); + if (terminal().getTabsNamingMode() == vtbackend::TabsNamingMode::Title) + return terminal().windowTitle(); + return std::nullopt; + } /// Starts the VT background thread. void start(); diff --git a/src/contour/TerminalSessionManager.h b/src/contour/TerminalSessionManager.h index cfc54b64c0..02a466c1b5 100644 --- a/src/contour/TerminalSessionManager.h +++ b/src/contour/TerminalSessionManager.h @@ -27,7 +27,6 @@ class TerminalSessionManager: public QAbstractListModel TerminalSessionManager(ContourGuiApp& app); contour::TerminalSession* createSessionInBackground(); - contour::TerminalSession* activateSession(TerminalSession* session, bool isNewSession = false); Q_INVOKABLE contour::TerminalSession* createSession(); @@ -54,7 +53,10 @@ class TerminalSessionManager: public QAbstractListModel display::TerminalDisplay* display = nullptr; TerminalSession* getSession() { return _sessions[0]; } + void Update() { updateStatusLine(); } + private: + contour::TerminalSession* activateSession(TerminalSession* session, bool isNewSession = false); std::unique_ptr createPty(std::optional cwd); [[nodiscard]] std::optional getSessionIndexOf(TerminalSession* session) const noexcept @@ -73,9 +75,15 @@ class TerminalSessionManager: public QAbstractListModel { if (!_activeSession) return; - _activeSession->terminal().setGuiTabInfoForStatusLine(vtbackend::TabsInfo { - .tabCount = _sessions.size(), + .tabs = std::ranges::transform_view(_sessions, + [](auto* session) { + return vtbackend::TabsInfo::Tab { + .name = session->name(), + .color = vtbackend::RGBColor { 0, 0, 0 }, + }; + }) + | ranges::to(), .activeTabPosition = 1 + getSessionIndexOf(_activeSession).value_or(0), }); } diff --git a/src/vtbackend/Settings.h b/src/vtbackend/Settings.h index 74be100ea5..1fa4dea995 100644 --- a/src/vtbackend/Settings.h +++ b/src/vtbackend/Settings.h @@ -24,6 +24,12 @@ struct RefreshInterval explicit RefreshInterval(RefreshRate rate): value { static_cast(1000.0 / rate.value) } {} }; +enum class TabsNamingMode : uint8_t +{ + Indexing, + Title +}; + /// Terminal settings, enabling hardware reset to be easier implemented. struct Settings { @@ -91,6 +97,8 @@ struct Settings bool fromSearchIntoInsertMode = true; bool isInsertAfterYank = false; + TabsNamingMode tabNamingMode = TabsNamingMode::Indexing; + // TODO: we could configure also the number of lines of the host writable statusline and indicator // statusline. }; diff --git a/src/vtbackend/StatusLineBuilder.cpp b/src/vtbackend/StatusLineBuilder.cpp index 2774375421..3dbe605b22 100644 --- a/src/vtbackend/StatusLineBuilder.cpp +++ b/src/vtbackend/StatusLineBuilder.cpp @@ -15,6 +15,7 @@ #include #include #include +#include using namespace std::string_view_literals; @@ -156,6 +157,7 @@ std::optional makeStatusLineItem( styles, activeColor, activeBackground, + std::nullopt, // separator }; } @@ -443,10 +445,10 @@ struct VTSerializer auto const tabsInfo = vt.guiTabsInfoForStatusLine(); std::string fragment; - for (const auto position: std::views::iota(1u, tabsInfo.tabCount + 1)) + for (const auto position: std::views::iota(1u, tabsInfo.tabs.size() + 1)) { if (!fragment.empty()) - fragment += ' '; + fragment += tabs.separator.value_or("|"); auto const isActivePosition = position == tabsInfo.activeTabPosition; auto const activePositionStylized = @@ -459,7 +461,10 @@ struct VTSerializer fragment += makeBackgroundColor(tabs.activeBackground); } - fragment += std::to_string(position); + if (tabsInfo.tabs[position - 1].name) + fragment += tabsInfo.tabs[position - 1].name.value(); + else + fragment += std::to_string(position); if (activePositionStylized) fragment += SGRRESTORE(); diff --git a/src/vtbackend/StatusLineBuilder.h b/src/vtbackend/StatusLineBuilder.h index fe64882920..5f6f76d266 100644 --- a/src/vtbackend/StatusLineBuilder.h +++ b/src/vtbackend/StatusLineBuilder.h @@ -43,6 +43,7 @@ namespace StatusLineDefinitions { std::optional activeColor; std::optional activeBackground; + std::optional separator; }; using Item = std::variant< diff --git a/src/vtbackend/Terminal.cpp b/src/vtbackend/Terminal.cpp index d9fd970c90..3100607759 100644 --- a/src/vtbackend/Terminal.cpp +++ b/src/vtbackend/Terminal.cpp @@ -1562,11 +1562,21 @@ void Terminal::setWindowTitle(string_view title) _eventListener.setWindowTitle(title); } +void Terminal::setTabName(string_view title) +{ + _tabName = title; +} + std::string const& Terminal::windowTitle() const noexcept { return _windowTitle; } +std::optional Terminal::tabName() const noexcept +{ + return _tabName; +} + void Terminal::saveWindowTitle() { _savedWindowTitles.push(_windowTitle); diff --git a/src/vtbackend/Terminal.h b/src/vtbackend/Terminal.h index c2676b7b56..dd31ffff90 100644 --- a/src/vtbackend/Terminal.h +++ b/src/vtbackend/Terminal.h @@ -42,6 +42,7 @@ #include #include #include +#include namespace vtbackend { @@ -209,7 +210,13 @@ class TraceHandler: public SequenceHandler struct TabsInfo { - size_t tabCount = 1; + struct Tab + { + std::optional name; + Color color; + }; + + std::vector tabs; size_t activeTabPosition = 1; }; @@ -246,6 +253,7 @@ class Terminal virtual void requestWindowResize(Width, Height) {} virtual void requestShowHostWritableStatusLine() {} virtual void setWindowTitle(std::string_view /*title*/) {} + virtual void setTabName(std::string_view /*title*/) {} virtual void setTerminalProfile(std::string const& /*configProfileName*/) {} virtual void discardImage(Image const&) {} virtual void inputModeChanged(ViMode /*mode*/) {} @@ -276,6 +284,7 @@ class Terminal void requestWindowResize(Width, Height) override {} void requestShowHostWritableStatusLine() override {} void setWindowTitle(std::string_view /*title*/) override {} + void setTabName(std::string_view /*title*/) override {} void setTerminalProfile(std::string const& /*configProfileName*/) override {} void discardImage(Image const&) override {} void inputModeChanged(ViMode /*mode*/) override {} @@ -797,7 +806,9 @@ class Terminal void setMouseTransport(MouseTransport transport); void setMouseWheelMode(InputGenerator::MouseWheelMode mode); void setWindowTitle(std::string_view title); + void setTabName(std::string_view title); [[nodiscard]] std::string const& windowTitle() const noexcept; + [[nodiscard]] std::optional tabName() const noexcept; [[nodiscard]] bool focused() const noexcept { return _focused; } [[nodiscard]] Search& search() noexcept { return _search; } [[nodiscard]] Search const& search() const noexcept { return _search; } @@ -966,7 +977,9 @@ class Terminal void resetStatusLineDefinition(); TabsInfo guiTabsInfoForStatusLine() const noexcept { return _guiTabInfoForStatusLine; } - void setGuiTabInfoForStatusLine(TabsInfo info) { _guiTabInfoForStatusLine = info; } + void setGuiTabInfoForStatusLine(TabsInfo&& info) { _guiTabInfoForStatusLine = std::move(info); } + + TabsNamingMode getTabsNamingMode() const noexcept { return _settings.tabNamingMode; } private: void mainLoop(); @@ -1080,7 +1093,9 @@ class Terminal Viewport _viewport; StatusLineDefinition _indicatorStatusLineDefinition; + // {{{ tabs info TabsInfo _guiTabInfoForStatusLine; + // }}} // {{{ selection states std::unique_ptr _selection; @@ -1165,6 +1180,8 @@ class Terminal std::string _windowTitle {}; std::stack _savedWindowTitles {}; + std::optional _tabName {}; + struct ModeDependantSequenceHandler { Terminal& terminal;